From 5890b023a549e7ba6b0c563cdf730a91c2de6fae Mon Sep 17 00:00:00 2001 From: Carlos Amedee Date: Thu, 9 May 2024 10:45:01 -0400 Subject: internal/trace: move v2 tracer into trace directory This change moves the v2 tracer into the trace directory. Updates #67367 Change-Id: I3657b4227002cb00fdf29c797434800ea796715e Reviewed-on: https://go-review.googlesource.com/c/go/+/584538 Reviewed-by: Michael Knyszek LUCI-TryBot-Result: Go LUCI --- src/internal/trace/base.go | 274 ++ src/internal/trace/batch.go | 111 + src/internal/trace/batchcursor.go | 174 + src/internal/trace/batchcursor_test.go | 126 + src/internal/trace/event.go | 863 ++++ src/internal/trace/event/event.go | 102 + src/internal/trace/event/go122/event.go | 511 +++ src/internal/trace/event/requirements.go | 26 + src/internal/trace/event_test.go | 43 + src/internal/trace/export_test.go | 7 + src/internal/trace/gc.go | 43 +- src/internal/trace/gc_test.go | 38 +- src/internal/trace/generation.go | 446 +++ src/internal/trace/internal/oldtrace/order.go | 186 + src/internal/trace/internal/oldtrace/parser.go | 1540 +++++++ .../trace/internal/oldtrace/parser_test.go | 151 + .../internal/oldtrace/testdata/fmt_1_21_pprof_good | Bin 0 -> 25772 bytes .../internal/oldtrace/testdata/http_1_19_good | Bin 0 -> 28172 bytes .../internal/oldtrace/testdata/http_1_21_good | Bin 0 -> 6744 bytes .../internal/oldtrace/testdata/stress_1_11_good | Bin 0 -> 370129 bytes .../internal/oldtrace/testdata/stress_1_19_good | Bin 0 -> 322338 bytes .../internal/oldtrace/testdata/stress_1_21_good | Bin 0 -> 353725 bytes .../oldtrace/testdata/stress_start_stop_1_11_good | Bin 0 -> 4882 bytes .../oldtrace/testdata/stress_start_stop_1_19_good | Bin 0 -> 7448 bytes .../oldtrace/testdata/stress_start_stop_1_21_good | Bin 0 -> 5002 bytes .../oldtrace/testdata/user_task_region_1_11_good | Bin 0 -> 2000 bytes .../oldtrace/testdata/user_task_region_1_19_good | Bin 0 -> 1922 bytes .../oldtrace/testdata/user_task_region_1_21_good | Bin 0 -> 2404 bytes src/internal/trace/internal/testgen/go122/trace.go | 401 ++ src/internal/trace/mud_test.go | 13 + src/internal/trace/oldtrace.go | 568 +++ src/internal/trace/oldtrace_test.go | 89 + src/internal/trace/order.go | 1399 +++++++ src/internal/trace/order_test.go | 34 + src/internal/trace/raw/doc.go | 66 + src/internal/trace/raw/event.go | 60 + src/internal/trace/raw/reader.go | 110 + src/internal/trace/raw/textreader.go | 217 + src/internal/trace/raw/textwriter.go | 39 + src/internal/trace/raw/writer.go | 75 + src/internal/trace/reader.go | 237 ++ src/internal/trace/reader_test.go | 172 + src/internal/trace/resources.go | 274 ++ src/internal/trace/summary.go | 167 +- src/internal/trace/summary_test.go | 148 +- src/internal/trace/testdata/README.md | 38 + .../testdata/cmd/cmd/gotraceeventstats/main.go | 136 + src/internal/trace/testdata/cmd/gotraceraw/main.go | 86 + .../trace/testdata/cmd/gotracevalidate/main.go | 53 + .../testdata/fuzz/FuzzReader/0cb1786dee0f090b | 2 + .../testdata/fuzz/FuzzReader/1e45307d5b2ec36d | 2 + .../testdata/fuzz/FuzzReader/2b05796f9b2fc48d | 2 + .../testdata/fuzz/FuzzReader/2b9be9aebe08d511 | 2 + .../testdata/fuzz/FuzzReader/344331b314da0b08 | 2 + .../testdata/fuzz/FuzzReader/365d7b5b633b3f97 | 2 + .../testdata/fuzz/FuzzReader/4055b17cae1a3443 | 2 + .../testdata/fuzz/FuzzReader/4d9ddc909984e871 | 2 + .../testdata/fuzz/FuzzReader/56f073e57903588c | 2 + .../testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 | 2 + .../testdata/fuzz/FuzzReader/aeb749b6bc317b66 | 2 + .../fuzz/FuzzReader/closing-unknown-region | 2 + .../testdata/fuzz/FuzzReader/d478e18d2d6756b7 | 2 + .../testdata/fuzz/FuzzReader/d91203cd397aa0bc | 2 + .../testdata/fuzz/FuzzReader/invalid-proc-state | 2 + .../trace/testdata/fuzz/FuzzReader/large-id | 2 + .../testdata/fuzz/FuzzReader/malformed-timestamp | 2 + src/internal/trace/testdata/generate.go | 6 + .../go122-confuse-seq-across-generations.go | 62 + .../go122-create-syscall-reuse-thread-id.go | 61 + .../generators/go122-create-syscall-with-p.go | 52 + .../generators/go122-fail-first-gen-first.go | 44 + .../go122-go-create-without-running-g.go | 33 + .../go122-syscall-steal-proc-ambiguous.go | 46 + ...go122-syscall-steal-proc-gen-boundary-bare-m.go | 33 + ...-proc-gen-boundary-reacquire-new-proc-bare-m.go | 34 + ...l-steal-proc-gen-boundary-reacquire-new-proc.go | 36 + .../go122-syscall-steal-proc-gen-boundary.go | 35 + ...syscall-steal-proc-reacquire-new-proc-bare-m.go | 34 + .../go122-syscall-steal-proc-reacquire-new-proc.go | 36 + .../generators/go122-syscall-steal-proc-self.go | 37 + .../go122-syscall-steal-proc-simple-bare-m.go | 32 + .../generators/go122-syscall-steal-proc-simple.go | 34 + .../go122-syscall-steal-proc-sitting-in-syscall.go | 32 + .../generators/go122-task-across-generations.go | 41 + src/internal/trace/testdata/mktests.go | 160 + .../trace/testdata/testprog/annotations-stress.go | 84 + .../trace/testdata/testprog/annotations.go | 60 + .../trace/testdata/testprog/cgo-callback.go | 80 + .../trace/testdata/testprog/cpu-profile.go | 137 + .../trace/testdata/testprog/futile-wakeup.go | 84 + src/internal/trace/testdata/testprog/gc-stress.go | 80 + src/internal/trace/testdata/testprog/gomaxprocs.go | 46 + src/internal/trace/testdata/testprog/iter-pull.go | 85 + .../trace/testdata/testprog/many-start-stop.go | 38 + src/internal/trace/testdata/testprog/stacks.go | 129 + .../trace/testdata/testprog/stress-start-stop.go | 166 + src/internal/trace/testdata/testprog/stress.go | 146 + .../trace/testdata/testprog/wait-on-pipe.go | 66 + .../testdata/tests/go122-annotations-stress.test | 1179 ++++++ .../trace/testdata/tests/go122-annotations.test | 299 ++ .../go122-confuse-seq-across-generations.test | 36 + .../go122-create-syscall-reuse-thread-id.test | 23 + .../tests/go122-create-syscall-with-p.test | 22 + .../testdata/tests/go122-fail-first-gen-first.test | 9 + .../trace/testdata/tests/go122-gc-stress.test | 4207 ++++++++++++++++++++ .../tests/go122-go-create-without-running-g.test | 17 + .../tests/go122-syscall-steal-proc-ambiguous.test | 21 + ...122-syscall-steal-proc-gen-boundary-bare-m.test | 17 + ...roc-gen-boundary-reacquire-new-proc-bare-m.test | 18 + ...steal-proc-gen-boundary-reacquire-new-proc.test | 20 + .../go122-syscall-steal-proc-gen-boundary.test | 19 + ...scall-steal-proc-reacquire-new-proc-bare-m.test | 19 + ...o122-syscall-steal-proc-reacquire-new-proc.test | 21 + .../tests/go122-syscall-steal-proc-self.test | 17 + .../go122-syscall-steal-proc-simple-bare-m.test | 17 + .../tests/go122-syscall-steal-proc-simple.test | 19 + ...o122-syscall-steal-proc-sitting-in-syscall.test | 15 + .../tests/go122-task-across-generations.test | 26 + src/internal/trace/testtrace/expectation.go | 81 + src/internal/trace/testtrace/format.go | 56 + src/internal/trace/testtrace/validation.go | 379 ++ src/internal/trace/trace_test.go | 629 +++ src/internal/trace/v2/base.go | 274 -- src/internal/trace/v2/batch.go | 111 - src/internal/trace/v2/batchcursor.go | 174 - src/internal/trace/v2/batchcursor_test.go | 126 - src/internal/trace/v2/event.go | 863 ---- src/internal/trace/v2/event/event.go | 102 - src/internal/trace/v2/event/go122/event.go | 511 --- src/internal/trace/v2/event/requirements.go | 26 - src/internal/trace/v2/event_test.go | 43 - src/internal/trace/v2/generation.go | 446 --- src/internal/trace/v2/internal/oldtrace/order.go | 186 - src/internal/trace/v2/internal/oldtrace/parser.go | 1540 ------- .../trace/v2/internal/oldtrace/parser_test.go | 151 - .../internal/oldtrace/testdata/fmt_1_21_pprof_good | Bin 25772 -> 0 bytes .../v2/internal/oldtrace/testdata/http_1_19_good | Bin 28172 -> 0 bytes .../v2/internal/oldtrace/testdata/http_1_21_good | Bin 6744 -> 0 bytes .../v2/internal/oldtrace/testdata/stress_1_11_good | Bin 370129 -> 0 bytes .../v2/internal/oldtrace/testdata/stress_1_19_good | Bin 322338 -> 0 bytes .../v2/internal/oldtrace/testdata/stress_1_21_good | Bin 353725 -> 0 bytes .../oldtrace/testdata/stress_start_stop_1_11_good | Bin 4882 -> 0 bytes .../oldtrace/testdata/stress_start_stop_1_19_good | Bin 7448 -> 0 bytes .../oldtrace/testdata/stress_start_stop_1_21_good | Bin 5002 -> 0 bytes .../oldtrace/testdata/user_task_region_1_11_good | Bin 2000 -> 0 bytes .../oldtrace/testdata/user_task_region_1_19_good | Bin 1922 -> 0 bytes .../oldtrace/testdata/user_task_region_1_21_good | Bin 2404 -> 0 bytes .../trace/v2/internal/testgen/go122/trace.go | 401 -- src/internal/trace/v2/mkexp.bash | 50 - src/internal/trace/v2/oldtrace.go | 568 --- src/internal/trace/v2/oldtrace_test.go | 89 - src/internal/trace/v2/order.go | 1399 ------- src/internal/trace/v2/order_test.go | 34 - src/internal/trace/v2/raw/doc.go | 66 - src/internal/trace/v2/raw/event.go | 60 - src/internal/trace/v2/raw/reader.go | 110 - src/internal/trace/v2/raw/textreader.go | 217 - src/internal/trace/v2/raw/textwriter.go | 39 - src/internal/trace/v2/raw/writer.go | 75 - src/internal/trace/v2/reader.go | 237 -- src/internal/trace/v2/reader_test.go | 172 - src/internal/trace/v2/resources.go | 274 -- src/internal/trace/v2/testdata/README.md | 38 - .../v2/testdata/cmd/gotraceeventstats/main.go | 136 - .../trace/v2/testdata/cmd/gotraceraw/main.go | 86 - .../trace/v2/testdata/cmd/gotracevalidate/main.go | 53 - .../v2/testdata/fuzz/FuzzReader/0cb1786dee0f090b | 2 - .../v2/testdata/fuzz/FuzzReader/1e45307d5b2ec36d | 2 - .../v2/testdata/fuzz/FuzzReader/2b05796f9b2fc48d | 2 - .../v2/testdata/fuzz/FuzzReader/2b9be9aebe08d511 | 2 - .../v2/testdata/fuzz/FuzzReader/344331b314da0b08 | 2 - .../v2/testdata/fuzz/FuzzReader/365d7b5b633b3f97 | 2 - .../v2/testdata/fuzz/FuzzReader/4055b17cae1a3443 | 2 - .../v2/testdata/fuzz/FuzzReader/4d9ddc909984e871 | 2 - .../v2/testdata/fuzz/FuzzReader/56f073e57903588c | 2 - .../v2/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 | 2 - .../v2/testdata/fuzz/FuzzReader/aeb749b6bc317b66 | 2 - .../fuzz/FuzzReader/closing-unknown-region | 2 - .../v2/testdata/fuzz/FuzzReader/d478e18d2d6756b7 | 2 - .../v2/testdata/fuzz/FuzzReader/d91203cd397aa0bc | 2 - .../v2/testdata/fuzz/FuzzReader/invalid-proc-state | 2 - .../trace/v2/testdata/fuzz/FuzzReader/large-id | 2 - .../testdata/fuzz/FuzzReader/malformed-timestamp | 2 - src/internal/trace/v2/testdata/generate.go | 6 - .../go122-confuse-seq-across-generations.go | 62 - .../go122-create-syscall-reuse-thread-id.go | 61 - .../generators/go122-create-syscall-with-p.go | 52 - .../generators/go122-fail-first-gen-first.go | 44 - .../go122-go-create-without-running-g.go | 33 - .../go122-syscall-steal-proc-ambiguous.go | 46 - ...go122-syscall-steal-proc-gen-boundary-bare-m.go | 33 - ...-proc-gen-boundary-reacquire-new-proc-bare-m.go | 34 - ...l-steal-proc-gen-boundary-reacquire-new-proc.go | 36 - .../go122-syscall-steal-proc-gen-boundary.go | 35 - ...syscall-steal-proc-reacquire-new-proc-bare-m.go | 34 - .../go122-syscall-steal-proc-reacquire-new-proc.go | 36 - .../generators/go122-syscall-steal-proc-self.go | 37 - .../go122-syscall-steal-proc-simple-bare-m.go | 32 - .../generators/go122-syscall-steal-proc-simple.go | 34 - .../go122-syscall-steal-proc-sitting-in-syscall.go | 32 - .../generators/go122-task-across-generations.go | 41 - src/internal/trace/v2/testdata/mktests.go | 160 - .../v2/testdata/testprog/annotations-stress.go | 84 - .../trace/v2/testdata/testprog/annotations.go | 60 - .../trace/v2/testdata/testprog/cgo-callback.go | 80 - .../trace/v2/testdata/testprog/cpu-profile.go | 137 - .../trace/v2/testdata/testprog/futile-wakeup.go | 84 - .../trace/v2/testdata/testprog/gc-stress.go | 80 - .../trace/v2/testdata/testprog/gomaxprocs.go | 46 - .../trace/v2/testdata/testprog/iter-pull.go | 85 - .../trace/v2/testdata/testprog/many-start-stop.go | 38 - src/internal/trace/v2/testdata/testprog/stacks.go | 129 - .../v2/testdata/testprog/stress-start-stop.go | 166 - src/internal/trace/v2/testdata/testprog/stress.go | 146 - .../trace/v2/testdata/testprog/wait-on-pipe.go | 66 - .../testdata/tests/go122-annotations-stress.test | 1179 ------ .../trace/v2/testdata/tests/go122-annotations.test | 299 -- .../go122-confuse-seq-across-generations.test | 36 - .../go122-create-syscall-reuse-thread-id.test | 23 - .../tests/go122-create-syscall-with-p.test | 22 - .../testdata/tests/go122-fail-first-gen-first.test | 9 - .../trace/v2/testdata/tests/go122-gc-stress.test | 4207 -------------------- .../tests/go122-go-create-without-running-g.test | 17 - .../tests/go122-syscall-steal-proc-ambiguous.test | 21 - ...122-syscall-steal-proc-gen-boundary-bare-m.test | 17 - ...roc-gen-boundary-reacquire-new-proc-bare-m.test | 18 - ...steal-proc-gen-boundary-reacquire-new-proc.test | 20 - .../go122-syscall-steal-proc-gen-boundary.test | 19 - ...scall-steal-proc-reacquire-new-proc-bare-m.test | 19 - ...o122-syscall-steal-proc-reacquire-new-proc.test | 21 - .../tests/go122-syscall-steal-proc-self.test | 17 - .../go122-syscall-steal-proc-simple-bare-m.test | 17 - .../tests/go122-syscall-steal-proc-simple.test | 19 - ...o122-syscall-steal-proc-sitting-in-syscall.test | 15 - .../tests/go122-task-across-generations.test | 26 - src/internal/trace/v2/testtrace/expectation.go | 81 - src/internal/trace/v2/testtrace/format.go | 56 - src/internal/trace/v2/testtrace/validation.go | 379 -- src/internal/trace/v2/trace_test.go | 629 --- src/internal/trace/v2/value.go | 53 - src/internal/trace/v2/version/version.go | 71 - src/internal/trace/value.go | 53 + src/internal/trace/version/version.go | 71 + 243 files changed, 18197 insertions(+), 18229 deletions(-) create mode 100644 src/internal/trace/base.go create mode 100644 src/internal/trace/batch.go create mode 100644 src/internal/trace/batchcursor.go create mode 100644 src/internal/trace/batchcursor_test.go create mode 100644 src/internal/trace/event.go create mode 100644 src/internal/trace/event/event.go create mode 100644 src/internal/trace/event/go122/event.go create mode 100644 src/internal/trace/event/requirements.go create mode 100644 src/internal/trace/event_test.go create mode 100644 src/internal/trace/export_test.go create mode 100644 src/internal/trace/generation.go create mode 100644 src/internal/trace/internal/oldtrace/order.go create mode 100644 src/internal/trace/internal/oldtrace/parser.go create mode 100644 src/internal/trace/internal/oldtrace/parser_test.go create mode 100644 src/internal/trace/internal/oldtrace/testdata/fmt_1_21_pprof_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/http_1_19_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/http_1_21_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/stress_1_11_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/stress_1_19_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/stress_1_21_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_11_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_19_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_21_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/user_task_region_1_11_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/user_task_region_1_19_good create mode 100644 src/internal/trace/internal/oldtrace/testdata/user_task_region_1_21_good create mode 100644 src/internal/trace/internal/testgen/go122/trace.go create mode 100644 src/internal/trace/oldtrace.go create mode 100644 src/internal/trace/oldtrace_test.go create mode 100644 src/internal/trace/order.go create mode 100644 src/internal/trace/order_test.go create mode 100644 src/internal/trace/raw/doc.go create mode 100644 src/internal/trace/raw/event.go create mode 100644 src/internal/trace/raw/reader.go create mode 100644 src/internal/trace/raw/textreader.go create mode 100644 src/internal/trace/raw/textwriter.go create mode 100644 src/internal/trace/raw/writer.go create mode 100644 src/internal/trace/reader.go create mode 100644 src/internal/trace/reader_test.go create mode 100644 src/internal/trace/resources.go create mode 100644 src/internal/trace/testdata/README.md create mode 100644 src/internal/trace/testdata/cmd/cmd/gotraceeventstats/main.go create mode 100644 src/internal/trace/testdata/cmd/gotraceraw/main.go create mode 100644 src/internal/trace/testdata/cmd/gotracevalidate/main.go create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/0cb1786dee0f090b create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/1e45307d5b2ec36d create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/2b05796f9b2fc48d create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/2b9be9aebe08d511 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/344331b314da0b08 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/365d7b5b633b3f97 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/4055b17cae1a3443 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/4d9ddc909984e871 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/56f073e57903588c create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/aeb749b6bc317b66 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/closing-unknown-region create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/d478e18d2d6756b7 create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/d91203cd397aa0bc create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/invalid-proc-state create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/large-id create mode 100644 src/internal/trace/testdata/fuzz/FuzzReader/malformed-timestamp create mode 100644 src/internal/trace/testdata/generate.go create mode 100644 src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go create mode 100644 src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go create mode 100644 src/internal/trace/testdata/generators/go122-create-syscall-with-p.go create mode 100644 src/internal/trace/testdata/generators/go122-fail-first-gen-first.go create mode 100644 src/internal/trace/testdata/generators/go122-go-create-without-running-g.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go create mode 100644 src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go create mode 100644 src/internal/trace/testdata/generators/go122-task-across-generations.go create mode 100644 src/internal/trace/testdata/mktests.go create mode 100644 src/internal/trace/testdata/testprog/annotations-stress.go create mode 100644 src/internal/trace/testdata/testprog/annotations.go create mode 100644 src/internal/trace/testdata/testprog/cgo-callback.go create mode 100644 src/internal/trace/testdata/testprog/cpu-profile.go create mode 100644 src/internal/trace/testdata/testprog/futile-wakeup.go create mode 100644 src/internal/trace/testdata/testprog/gc-stress.go create mode 100644 src/internal/trace/testdata/testprog/gomaxprocs.go create mode 100644 src/internal/trace/testdata/testprog/iter-pull.go create mode 100644 src/internal/trace/testdata/testprog/many-start-stop.go create mode 100644 src/internal/trace/testdata/testprog/stacks.go create mode 100644 src/internal/trace/testdata/testprog/stress-start-stop.go create mode 100644 src/internal/trace/testdata/testprog/stress.go create mode 100644 src/internal/trace/testdata/testprog/wait-on-pipe.go create mode 100644 src/internal/trace/testdata/tests/go122-annotations-stress.test create mode 100644 src/internal/trace/testdata/tests/go122-annotations.test create mode 100644 src/internal/trace/testdata/tests/go122-confuse-seq-across-generations.test create mode 100644 src/internal/trace/testdata/tests/go122-create-syscall-reuse-thread-id.test create mode 100644 src/internal/trace/testdata/tests/go122-create-syscall-with-p.test create mode 100644 src/internal/trace/testdata/tests/go122-fail-first-gen-first.test create mode 100644 src/internal/trace/testdata/tests/go122-gc-stress.test create mode 100644 src/internal/trace/testdata/tests/go122-go-create-without-running-g.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-ambiguous.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-self.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple.test create mode 100644 src/internal/trace/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test create mode 100644 src/internal/trace/testdata/tests/go122-task-across-generations.test create mode 100644 src/internal/trace/testtrace/expectation.go create mode 100644 src/internal/trace/testtrace/format.go create mode 100644 src/internal/trace/testtrace/validation.go create mode 100644 src/internal/trace/trace_test.go delete mode 100644 src/internal/trace/v2/base.go delete mode 100644 src/internal/trace/v2/batch.go delete mode 100644 src/internal/trace/v2/batchcursor.go delete mode 100644 src/internal/trace/v2/batchcursor_test.go delete mode 100644 src/internal/trace/v2/event.go delete mode 100644 src/internal/trace/v2/event/event.go delete mode 100644 src/internal/trace/v2/event/go122/event.go delete mode 100644 src/internal/trace/v2/event/requirements.go delete mode 100644 src/internal/trace/v2/event_test.go delete mode 100644 src/internal/trace/v2/generation.go delete mode 100644 src/internal/trace/v2/internal/oldtrace/order.go delete mode 100644 src/internal/trace/v2/internal/oldtrace/parser.go delete mode 100644 src/internal/trace/v2/internal/oldtrace/parser_test.go delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/fmt_1_21_pprof_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/http_1_19_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/http_1_21_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/stress_1_11_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/stress_1_19_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/stress_1_21_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_11_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_19_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_21_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_11_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_19_good delete mode 100644 src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_21_good delete mode 100644 src/internal/trace/v2/internal/testgen/go122/trace.go delete mode 100755 src/internal/trace/v2/mkexp.bash delete mode 100644 src/internal/trace/v2/oldtrace.go delete mode 100644 src/internal/trace/v2/oldtrace_test.go delete mode 100644 src/internal/trace/v2/order.go delete mode 100644 src/internal/trace/v2/order_test.go delete mode 100644 src/internal/trace/v2/raw/doc.go delete mode 100644 src/internal/trace/v2/raw/event.go delete mode 100644 src/internal/trace/v2/raw/reader.go delete mode 100644 src/internal/trace/v2/raw/textreader.go delete mode 100644 src/internal/trace/v2/raw/textwriter.go delete mode 100644 src/internal/trace/v2/raw/writer.go delete mode 100644 src/internal/trace/v2/reader.go delete mode 100644 src/internal/trace/v2/reader_test.go delete mode 100644 src/internal/trace/v2/resources.go delete mode 100644 src/internal/trace/v2/testdata/README.md delete mode 100644 src/internal/trace/v2/testdata/cmd/gotraceeventstats/main.go delete mode 100644 src/internal/trace/v2/testdata/cmd/gotraceraw/main.go delete mode 100644 src/internal/trace/v2/testdata/cmd/gotracevalidate/main.go delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/0cb1786dee0f090b delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/1e45307d5b2ec36d delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/2b05796f9b2fc48d delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/2b9be9aebe08d511 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/344331b314da0b08 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/365d7b5b633b3f97 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/4055b17cae1a3443 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/4d9ddc909984e871 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/56f073e57903588c delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/aeb749b6bc317b66 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/closing-unknown-region delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/d478e18d2d6756b7 delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/d91203cd397aa0bc delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/invalid-proc-state delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/large-id delete mode 100644 src/internal/trace/v2/testdata/fuzz/FuzzReader/malformed-timestamp delete mode 100644 src/internal/trace/v2/testdata/generate.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-confuse-seq-across-generations.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-create-syscall-reuse-thread-id.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-create-syscall-with-p.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-fail-first-gen-first.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-go-create-without-running-g.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-ambiguous.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-self.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go delete mode 100644 src/internal/trace/v2/testdata/generators/go122-task-across-generations.go delete mode 100644 src/internal/trace/v2/testdata/mktests.go delete mode 100644 src/internal/trace/v2/testdata/testprog/annotations-stress.go delete mode 100644 src/internal/trace/v2/testdata/testprog/annotations.go delete mode 100644 src/internal/trace/v2/testdata/testprog/cgo-callback.go delete mode 100644 src/internal/trace/v2/testdata/testprog/cpu-profile.go delete mode 100644 src/internal/trace/v2/testdata/testprog/futile-wakeup.go delete mode 100644 src/internal/trace/v2/testdata/testprog/gc-stress.go delete mode 100644 src/internal/trace/v2/testdata/testprog/gomaxprocs.go delete mode 100644 src/internal/trace/v2/testdata/testprog/iter-pull.go delete mode 100644 src/internal/trace/v2/testdata/testprog/many-start-stop.go delete mode 100644 src/internal/trace/v2/testdata/testprog/stacks.go delete mode 100644 src/internal/trace/v2/testdata/testprog/stress-start-stop.go delete mode 100644 src/internal/trace/v2/testdata/testprog/stress.go delete mode 100644 src/internal/trace/v2/testdata/testprog/wait-on-pipe.go delete mode 100644 src/internal/trace/v2/testdata/tests/go122-annotations-stress.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-annotations.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-confuse-seq-across-generations.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-create-syscall-reuse-thread-id.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-create-syscall-with-p.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-fail-first-gen-first.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-gc-stress.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-go-create-without-running-g.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-ambiguous.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-self.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test delete mode 100644 src/internal/trace/v2/testdata/tests/go122-task-across-generations.test delete mode 100644 src/internal/trace/v2/testtrace/expectation.go delete mode 100644 src/internal/trace/v2/testtrace/format.go delete mode 100644 src/internal/trace/v2/testtrace/validation.go delete mode 100644 src/internal/trace/v2/trace_test.go delete mode 100644 src/internal/trace/v2/value.go delete mode 100644 src/internal/trace/v2/version/version.go create mode 100644 src/internal/trace/value.go create mode 100644 src/internal/trace/version/version.go (limited to 'src/internal/trace') diff --git a/src/internal/trace/base.go b/src/internal/trace/base.go new file mode 100644 index 0000000000..4cbd3e64f1 --- /dev/null +++ b/src/internal/trace/base.go @@ -0,0 +1,274 @@ +// 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. + +// This file contains data types that all implementations of the trace format +// parser need to provide to the rest of the package. + +package trace + +import ( + "fmt" + "math" + "strings" + + "internal/trace/event" + "internal/trace/event/go122" + "internal/trace/version" +) + +// maxArgs is the maximum number of arguments for "plain" events, +// i.e. anything that could reasonably be represented as a baseEvent. +// +// TODO(mknyszek): This is only 6 instead of 5 because GoStatusStack +// has 5 arguments and needs to smuggle in a 6th. Figure out a way to +// shrink this in the future. +const maxArgs = 6 + +// timedEventArgs is an array that is able to hold the arguments for any +// timed event. +type timedEventArgs [maxArgs - 1]uint64 + +// baseEvent is the basic unprocessed event. This serves as a common +// fundamental data structure across. +type baseEvent struct { + typ event.Type + time Time + args timedEventArgs +} + +// extra returns a slice representing extra available space in args +// that the parser can use to pass data up into Event. +func (e *baseEvent) extra(v version.Version) []uint64 { + switch v { + case version.Go122: + return e.args[len(go122.Specs()[e.typ].Args)-1:] + } + panic(fmt.Sprintf("unsupported version: go 1.%d", v)) +} + +// evTable contains the per-generation data necessary to +// interpret an individual event. +type evTable struct { + freq frequency + strings dataTable[stringID, string] + stacks dataTable[stackID, stack] + pcs map[uint64]frame + + // extraStrings are strings that get generated during + // parsing but haven't come directly from the trace, so + // they don't appear in strings. + extraStrings []string + extraStringIDs map[string]extraStringID + nextExtra extraStringID + + // expData contains extra unparsed data that is accessible + // only to ExperimentEvent via an EventExperimental event. + expData map[event.Experiment]*ExperimentalData +} + +// addExtraString adds an extra string to the evTable and returns +// a unique ID for the string in the table. +func (t *evTable) addExtraString(s string) extraStringID { + if s == "" { + return 0 + } + if t.extraStringIDs == nil { + t.extraStringIDs = make(map[string]extraStringID) + } + if id, ok := t.extraStringIDs[s]; ok { + return id + } + t.nextExtra++ + id := t.nextExtra + t.extraStrings = append(t.extraStrings, s) + t.extraStringIDs[s] = id + return id +} + +// getExtraString returns the extra string for the provided ID. +// The ID must have been produced by addExtraString for this evTable. +func (t *evTable) getExtraString(id extraStringID) string { + if id == 0 { + return "" + } + return t.extraStrings[id-1] +} + +// dataTable is a mapping from EIs to Es. +type dataTable[EI ~uint64, E any] struct { + present []uint8 + dense []E + sparse map[EI]E +} + +// insert tries to add a mapping from id to s. +// +// Returns an error if a mapping for id already exists, regardless +// of whether or not s is the same in content. This should be used +// for validation during parsing. +func (d *dataTable[EI, E]) insert(id EI, data E) error { + if d.sparse == nil { + d.sparse = make(map[EI]E) + } + if existing, ok := d.get(id); ok { + return fmt.Errorf("multiple %Ts with the same ID: id=%d, new=%v, existing=%v", data, id, data, existing) + } + d.sparse[id] = data + return nil +} + +// compactify attempts to compact sparse into dense. +// +// This is intended to be called only once after insertions are done. +func (d *dataTable[EI, E]) compactify() { + if d.sparse == nil || len(d.dense) != 0 { + // Already compactified. + return + } + // Find the range of IDs. + maxID := EI(0) + minID := ^EI(0) + for id := range d.sparse { + if id > maxID { + maxID = id + } + if id < minID { + minID = id + } + } + if maxID >= math.MaxInt { + // We can't create a slice big enough to hold maxID elements + return + } + // We're willing to waste at most 2x memory. + if int(maxID-minID) > max(len(d.sparse), 2*len(d.sparse)) { + return + } + if int(minID) > len(d.sparse) { + return + } + size := int(maxID) + 1 + d.present = make([]uint8, (size+7)/8) + d.dense = make([]E, size) + for id, data := range d.sparse { + d.dense[id] = data + d.present[id/8] |= uint8(1) << (id % 8) + } + d.sparse = nil +} + +// get returns the E for id or false if it doesn't +// exist. This should be used for validation during parsing. +func (d *dataTable[EI, E]) get(id EI) (E, bool) { + if id == 0 { + return *new(E), true + } + if uint64(id) < uint64(len(d.dense)) { + if d.present[id/8]&(uint8(1)<<(id%8)) != 0 { + return d.dense[id], true + } + } else if d.sparse != nil { + if data, ok := d.sparse[id]; ok { + return data, true + } + } + return *new(E), false +} + +// forEach iterates over all ID/value pairs in the data table. +func (d *dataTable[EI, E]) forEach(yield func(EI, E) bool) bool { + for id, value := range d.dense { + if d.present[id/8]&(uint8(1)<<(id%8)) == 0 { + continue + } + if !yield(EI(id), value) { + return false + } + } + if d.sparse == nil { + return true + } + for id, value := range d.sparse { + if !yield(id, value) { + return false + } + } + return true +} + +// mustGet returns the E for id or panics if it fails. +// +// This should only be used if id has already been validated. +func (d *dataTable[EI, E]) mustGet(id EI) E { + data, ok := d.get(id) + if !ok { + panic(fmt.Sprintf("expected id %d in %T table", id, data)) + } + return data +} + +// frequency is nanoseconds per timestamp unit. +type frequency float64 + +// mul multiplies an unprocessed to produce a time in nanoseconds. +func (f frequency) mul(t timestamp) Time { + return Time(float64(t) * float64(f)) +} + +// stringID is an index into the string table for a generation. +type stringID uint64 + +// extraStringID is an index into the extra string table for a generation. +type extraStringID uint64 + +// stackID is an index into the stack table for a generation. +type stackID uint64 + +// cpuSample represents a CPU profiling sample captured by the trace. +type cpuSample struct { + schedCtx + time Time + stack stackID +} + +// asEvent produces a complete Event from a cpuSample. It needs +// the evTable from the generation that created it. +// +// We don't just store it as an Event in generation to minimize +// the amount of pointer data floating around. +func (s cpuSample) asEvent(table *evTable) Event { + // TODO(mknyszek): This is go122-specific, but shouldn't be. + // Generalize this in the future. + e := Event{ + table: table, + ctx: s.schedCtx, + base: baseEvent{ + typ: go122.EvCPUSample, + time: s.time, + }, + } + e.base.args[0] = uint64(s.stack) + return e +} + +// stack represents a goroutine stack sample. +type stack struct { + pcs []uint64 +} + +func (s stack) String() string { + var sb strings.Builder + for _, frame := range s.pcs { + fmt.Fprintf(&sb, "\t%#v\n", frame) + } + return sb.String() +} + +// frame represents a single stack frame. +type frame struct { + pc uint64 + funcID stringID + fileID stringID + line uint64 +} diff --git a/src/internal/trace/batch.go b/src/internal/trace/batch.go new file mode 100644 index 0000000000..2b47c9b72c --- /dev/null +++ b/src/internal/trace/batch.go @@ -0,0 +1,111 @@ +// 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. + +package trace + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + + "internal/trace/event" + "internal/trace/event/go122" +) + +// timestamp is an unprocessed timestamp. +type timestamp uint64 + +// batch represents a batch of trace events. +// It is unparsed except for its header. +type batch struct { + m ThreadID + time timestamp + data []byte + exp event.Experiment +} + +func (b *batch) isStringsBatch() bool { + return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStrings +} + +func (b *batch) isStacksBatch() bool { + return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStacks +} + +func (b *batch) isCPUSamplesBatch() bool { + return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvCPUSamples +} + +func (b *batch) isFreqBatch() bool { + return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvFrequency +} + +// readBatch reads the next full batch from r. +func readBatch(r interface { + io.Reader + io.ByteReader +}) (batch, uint64, error) { + // Read batch header byte. + b, err := r.ReadByte() + if err != nil { + return batch{}, 0, err + } + if typ := event.Type(b); typ != go122.EvEventBatch && typ != go122.EvExperimentalBatch { + return batch{}, 0, fmt.Errorf("expected batch event, got %s", go122.EventString(typ)) + } + + // Read the experiment of we have one. + exp := event.NoExperiment + if event.Type(b) == go122.EvExperimentalBatch { + e, err := r.ReadByte() + if err != nil { + return batch{}, 0, err + } + exp = event.Experiment(e) + } + + // Read the batch header: gen (generation), thread (M) ID, base timestamp + // for the batch. + gen, err := binary.ReadUvarint(r) + if err != nil { + return batch{}, gen, fmt.Errorf("error reading batch gen: %w", err) + } + m, err := binary.ReadUvarint(r) + if err != nil { + return batch{}, gen, fmt.Errorf("error reading batch M ID: %w", err) + } + ts, err := binary.ReadUvarint(r) + if err != nil { + return batch{}, gen, fmt.Errorf("error reading batch timestamp: %w", err) + } + + // Read in the size of the batch to follow. + size, err := binary.ReadUvarint(r) + if err != nil { + return batch{}, gen, fmt.Errorf("error reading batch size: %w", err) + } + if size > go122.MaxBatchSize { + return batch{}, gen, fmt.Errorf("invalid batch size %d, maximum is %d", size, go122.MaxBatchSize) + } + + // Copy out the batch for later processing. + var data bytes.Buffer + data.Grow(int(size)) + n, err := io.CopyN(&data, r, int64(size)) + if n != int64(size) { + return batch{}, gen, fmt.Errorf("failed to read full batch: read %d but wanted %d", n, size) + } + if err != nil { + return batch{}, gen, fmt.Errorf("copying batch data: %w", err) + } + + // Return the batch. + return batch{ + m: ThreadID(m), + time: timestamp(ts), + data: data.Bytes(), + exp: exp, + }, gen, nil +} diff --git a/src/internal/trace/batchcursor.go b/src/internal/trace/batchcursor.go new file mode 100644 index 0000000000..66d297ee33 --- /dev/null +++ b/src/internal/trace/batchcursor.go @@ -0,0 +1,174 @@ +// 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. + +package trace + +import ( + "cmp" + "encoding/binary" + "fmt" + + "internal/trace/event" + "internal/trace/event/go122" +) + +type batchCursor struct { + m ThreadID + lastTs Time + idx int // next index into []batch + dataOff int // next index into batch.data + ev baseEvent // last read event +} + +func (b *batchCursor) nextEvent(batches []batch, freq frequency) (ok bool, err error) { + // Batches should generally always have at least one event, + // but let's be defensive about that and accept empty batches. + for b.idx < len(batches) && len(batches[b.idx].data) == b.dataOff { + b.idx++ + b.dataOff = 0 + b.lastTs = 0 + } + // Have we reached the end of the batches? + if b.idx == len(batches) { + return false, nil + } + // Initialize lastTs if it hasn't been yet. + if b.lastTs == 0 { + b.lastTs = freq.mul(batches[b.idx].time) + } + // Read an event out. + n, tsdiff, err := readTimedBaseEvent(batches[b.idx].data[b.dataOff:], &b.ev) + if err != nil { + return false, err + } + // Complete the timestamp from the cursor's last timestamp. + b.ev.time = freq.mul(tsdiff) + b.lastTs + + // Move the cursor's timestamp forward. + b.lastTs = b.ev.time + + // Move the cursor forward. + b.dataOff += n + return true, nil +} + +func (b *batchCursor) compare(a *batchCursor) int { + return cmp.Compare(b.ev.time, a.ev.time) +} + +// readTimedBaseEvent reads out the raw event data from b +// into e. It does not try to interpret the arguments +// but it does validate that the event is a regular +// event with a timestamp (vs. a structural event). +// +// It requires that the event its reading be timed, which must +// be the case for every event in a plain EventBatch. +func readTimedBaseEvent(b []byte, e *baseEvent) (int, timestamp, error) { + // Get the event type. + typ := event.Type(b[0]) + specs := go122.Specs() + if int(typ) >= len(specs) { + return 0, 0, fmt.Errorf("found invalid event type: %v", typ) + } + e.typ = typ + + // Get spec. + spec := &specs[typ] + if len(spec.Args) == 0 || !spec.IsTimedEvent { + return 0, 0, fmt.Errorf("found event without a timestamp: type=%v", typ) + } + n := 1 + + // Read timestamp diff. + ts, nb := binary.Uvarint(b[n:]) + if nb <= 0 { + return 0, 0, fmt.Errorf("found invalid uvarint for timestamp") + } + n += nb + + // Read the rest of the arguments. + for i := 0; i < len(spec.Args)-1; i++ { + arg, nb := binary.Uvarint(b[n:]) + if nb <= 0 { + return 0, 0, fmt.Errorf("found invalid uvarint") + } + e.args[i] = arg + n += nb + } + return n, timestamp(ts), nil +} + +func heapInsert(heap []*batchCursor, bc *batchCursor) []*batchCursor { + // Add the cursor to the end of the heap. + heap = append(heap, bc) + + // Sift the new entry up to the right place. + heapSiftUp(heap, len(heap)-1) + return heap +} + +func heapUpdate(heap []*batchCursor, i int) { + // Try to sift up. + if heapSiftUp(heap, i) != i { + return + } + // Try to sift down, if sifting up failed. + heapSiftDown(heap, i) +} + +func heapRemove(heap []*batchCursor, i int) []*batchCursor { + // Sift index i up to the root, ignoring actual values. + for i > 0 { + heap[(i-1)/2], heap[i] = heap[i], heap[(i-1)/2] + i = (i - 1) / 2 + } + // Swap the root with the last element, then remove it. + heap[0], heap[len(heap)-1] = heap[len(heap)-1], heap[0] + heap = heap[:len(heap)-1] + // Sift the root down. + heapSiftDown(heap, 0) + return heap +} + +func heapSiftUp(heap []*batchCursor, i int) int { + for i > 0 && heap[(i-1)/2].ev.time > heap[i].ev.time { + heap[(i-1)/2], heap[i] = heap[i], heap[(i-1)/2] + i = (i - 1) / 2 + } + return i +} + +func heapSiftDown(heap []*batchCursor, i int) int { + for { + m := min3(heap, i, 2*i+1, 2*i+2) + if m == i { + // Heap invariant already applies. + break + } + heap[i], heap[m] = heap[m], heap[i] + i = m + } + return i +} + +func min3(b []*batchCursor, i0, i1, i2 int) int { + minIdx := i0 + minT := maxTime + if i0 < len(b) { + minT = b[i0].ev.time + } + if i1 < len(b) { + if t := b[i1].ev.time; t < minT { + minT = t + minIdx = i1 + } + } + if i2 < len(b) { + if t := b[i2].ev.time; t < minT { + minT = t + minIdx = i2 + } + } + return minIdx +} diff --git a/src/internal/trace/batchcursor_test.go b/src/internal/trace/batchcursor_test.go new file mode 100644 index 0000000000..69731e5254 --- /dev/null +++ b/src/internal/trace/batchcursor_test.go @@ -0,0 +1,126 @@ +// 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. + +package trace + +import ( + "fmt" + "strings" + "testing" + + "slices" +) + +func TestHeap(t *testing.T) { + var heap []*batchCursor + + // Insert a bunch of values into the heap. + checkHeap(t, heap) + heap = heapInsert(heap, makeBatchCursor(5)) + checkHeap(t, heap) + for i := int64(-20); i < 20; i++ { + heap = heapInsert(heap, makeBatchCursor(i)) + checkHeap(t, heap) + } + + // Update an element in the middle to be the new minimum. + for i := range heap { + if heap[i].ev.time == 5 { + heap[i].ev.time = -21 + heapUpdate(heap, i) + break + } + } + checkHeap(t, heap) + if heap[0].ev.time != -21 { + t.Fatalf("heap update failed, expected %d as heap min: %s", -21, heapDebugString(heap)) + } + + // Update the minimum element to be smaller. There should be no change. + heap[0].ev.time = -22 + heapUpdate(heap, 0) + checkHeap(t, heap) + if heap[0].ev.time != -22 { + t.Fatalf("heap update failed, expected %d as heap min: %s", -22, heapDebugString(heap)) + } + + // Update the last element to be larger. There should be no change. + heap[len(heap)-1].ev.time = 21 + heapUpdate(heap, len(heap)-1) + checkHeap(t, heap) + if heap[len(heap)-1].ev.time != 21 { + t.Fatalf("heap update failed, expected %d as heap min: %s", 21, heapDebugString(heap)) + } + + // Update the last element to be smaller. + heap[len(heap)-1].ev.time = 7 + heapUpdate(heap, len(heap)-1) + checkHeap(t, heap) + if heap[len(heap)-1].ev.time == 21 { + t.Fatalf("heap update failed, unexpected %d as heap min: %s", 21, heapDebugString(heap)) + } + + // Remove an element in the middle. + for i := range heap { + if heap[i].ev.time == 5 { + heap = heapRemove(heap, i) + break + } + } + checkHeap(t, heap) + for i := range heap { + if heap[i].ev.time == 5 { + t.Fatalf("failed to remove heap elem with time %d: %s", 5, heapDebugString(heap)) + } + } + + // Remove tail. + heap = heapRemove(heap, len(heap)-1) + checkHeap(t, heap) + + // Remove from the head, and make sure the result is sorted. + l := len(heap) + var removed []*batchCursor + for i := 0; i < l; i++ { + removed = append(removed, heap[0]) + heap = heapRemove(heap, 0) + checkHeap(t, heap) + } + if !slices.IsSortedFunc(removed, (*batchCursor).compare) { + t.Fatalf("heap elements not removed in sorted order, got: %s", heapDebugString(removed)) + } +} + +func makeBatchCursor(v int64) *batchCursor { + return &batchCursor{ev: baseEvent{time: Time(v)}} +} + +func heapDebugString(heap []*batchCursor) string { + var sb strings.Builder + fmt.Fprintf(&sb, "[") + for i := range heap { + if i != 0 { + fmt.Fprintf(&sb, ", ") + } + fmt.Fprintf(&sb, "%d", heap[i].ev.time) + } + fmt.Fprintf(&sb, "]") + return sb.String() +} + +func checkHeap(t *testing.T, heap []*batchCursor) { + t.Helper() + + for i := range heap { + if i == 0 { + continue + } + if heap[(i-1)/2].compare(heap[i]) > 0 { + t.Errorf("heap invariant not maintained between index %d and parent %d: %s", i, i/2, heapDebugString(heap)) + } + } + if t.Failed() { + t.FailNow() + } +} diff --git a/src/internal/trace/event.go b/src/internal/trace/event.go new file mode 100644 index 0000000000..e30e20ecbb --- /dev/null +++ b/src/internal/trace/event.go @@ -0,0 +1,863 @@ +// 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. + +package trace + +import ( + "fmt" + "math" + "strings" + "time" + + "internal/trace/event" + "internal/trace/event/go122" + "internal/trace/version" +) + +// EventKind indicates the kind of event this is. +// +// Use this information to obtain a more specific event that +// allows access to more detailed information. +type EventKind uint16 + +const ( + EventBad EventKind = iota + + // EventKindSync is an event that indicates a global synchronization + // point in the trace. At the point of a sync event, the + // trace reader can be certain that all resources (e.g. threads, + // goroutines) that have existed until that point have been enumerated. + EventSync + + // EventMetric is an event that represents the value of a metric at + // a particular point in time. + EventMetric + + // EventLabel attaches a label to a resource. + EventLabel + + // EventStackSample represents an execution sample, indicating what a + // thread/proc/goroutine was doing at a particular point in time via + // its backtrace. + // + // Note: Samples should be considered a close approximation of + // what a thread/proc/goroutine was executing at a given point in time. + // These events may slightly contradict the situation StateTransitions + // describe, so they should only be treated as a best-effort annotation. + EventStackSample + + // EventRangeBegin and EventRangeEnd are a pair of generic events representing + // a special range of time. Ranges are named and scoped to some resource + // (identified via ResourceKind). A range that has begun but has not ended + // is considered active. + // + // EvRangeBegin and EvRangeEnd will share the same name, and an End will always + // follow a Begin on the same instance of the resource. The associated + // resource ID can be obtained from the Event. ResourceNone indicates the + // range is globally scoped. That is, any goroutine/proc/thread can start or + // stop, but only one such range may be active at any given time. + // + // EventRangeActive is like EventRangeBegin, but indicates that the range was + // already active. In this case, the resource referenced may not be in the current + // context. + EventRangeBegin + EventRangeActive + EventRangeEnd + + // EvTaskBegin and EvTaskEnd are a pair of events representing a runtime/trace.Task. + EventTaskBegin + EventTaskEnd + + // EventRegionBegin and EventRegionEnd are a pair of events represent a runtime/trace.Region. + EventRegionBegin + EventRegionEnd + + // EventLog represents a runtime/trace.Log call. + EventLog + + // EventStateTransition represents a state change for some resource. + EventStateTransition + + // EventExperimental is an experimental event that is unvalidated and exposed in a raw form. + // Users are expected to understand the format and perform their own validation. These events + // may always be safely ignored. + EventExperimental +) + +// String returns a string form of the EventKind. +func (e EventKind) String() string { + if int(e) >= len(eventKindStrings) { + return eventKindStrings[0] + } + return eventKindStrings[e] +} + +var eventKindStrings = [...]string{ + EventBad: "Bad", + EventSync: "Sync", + EventMetric: "Metric", + EventLabel: "Label", + EventStackSample: "StackSample", + EventRangeBegin: "RangeBegin", + EventRangeActive: "RangeActive", + EventRangeEnd: "RangeEnd", + EventTaskBegin: "TaskBegin", + EventTaskEnd: "TaskEnd", + EventRegionBegin: "RegionBegin", + EventRegionEnd: "RegionEnd", + EventLog: "Log", + EventStateTransition: "StateTransition", + EventExperimental: "Experimental", +} + +const maxTime = Time(math.MaxInt64) + +// Time is a timestamp in nanoseconds. +// +// It corresponds to the monotonic clock on the platform that the +// trace was taken, and so is possible to correlate with timestamps +// for other traces taken on the same machine using the same clock +// (i.e. no reboots in between). +// +// The actual absolute value of the timestamp is only meaningful in +// relation to other timestamps from the same clock. +// +// BUG: Timestamps coming from traces on Windows platforms are +// only comparable with timestamps from the same trace. Timestamps +// across traces cannot be compared, because the system clock is +// not used as of Go 1.22. +// +// BUG: Traces produced by Go versions 1.21 and earlier cannot be +// compared with timestamps from other traces taken on the same +// machine. This is because the system clock was not used at all +// to collect those timestamps. +type Time int64 + +// Sub subtracts t0 from t, returning the duration in nanoseconds. +func (t Time) Sub(t0 Time) time.Duration { + return time.Duration(int64(t) - int64(t0)) +} + +// Metric provides details about a Metric event. +type Metric struct { + // Name is the name of the sampled metric. + // + // Names follow the same convention as metric names in the + // runtime/metrics package, meaning they include the unit. + // Names that match with the runtime/metrics package represent + // the same quantity. Note that this corresponds to the + // runtime/metrics package for the Go version this trace was + // collected for. + Name string + + // Value is the sampled value of the metric. + // + // The Value's Kind is tied to the name of the metric, and so is + // guaranteed to be the same for metric samples for the same metric. + Value Value +} + +// Label provides details about a Label event. +type Label struct { + // Label is the label applied to some resource. + Label string + + // Resource is the resource to which this label should be applied. + Resource ResourceID +} + +// Range provides details about a Range event. +type Range struct { + // Name is a human-readable name for the range. + // + // This name can be used to identify the end of the range for the resource + // its scoped to, because only one of each type of range may be active on + // a particular resource. The relevant resource should be obtained from the + // Event that produced these details. The corresponding RangeEnd will have + // an identical name. + Name string + + // Scope is the resource that the range is scoped to. + // + // For example, a ResourceGoroutine scope means that the same goroutine + // must have a start and end for the range, and that goroutine can only + // have one range of a particular name active at any given time. The + // ID that this range is scoped to may be obtained via Event.Goroutine. + // + // The ResourceNone scope means that the range is globally scoped. As a + // result, any goroutine/proc/thread may start or end the range, and only + // one such named range may be active globally at any given time. + // + // For RangeBegin and RangeEnd events, this will always reference some + // resource ID in the current execution context. For RangeActive events, + // this may reference a resource not in the current context. Prefer Scope + // over the current execution context. + Scope ResourceID +} + +// RangeAttributes provides attributes about a completed Range. +type RangeAttribute struct { + // Name is the human-readable name for the range. + Name string + + // Value is the value of the attribute. + Value Value +} + +// TaskID is the internal ID of a task used to disambiguate tasks (even if they +// are of the same type). +type TaskID uint64 + +const ( + // NoTask indicates the lack of a task. + NoTask = TaskID(^uint64(0)) + + // BackgroundTask is the global task that events are attached to if there was + // no other task in the context at the point the event was emitted. + BackgroundTask = TaskID(0) +) + +// Task provides details about a Task event. +type Task struct { + // ID is a unique identifier for the task. + // + // This can be used to associate the beginning of a task with its end. + ID TaskID + + // ParentID is the ID of the parent task. + Parent TaskID + + // Type is the taskType that was passed to runtime/trace.NewTask. + // + // May be "" if a task's TaskBegin event isn't present in the trace. + Type string +} + +// Region provides details about a Region event. +type Region struct { + // Task is the ID of the task this region is associated with. + Task TaskID + + // Type is the regionType that was passed to runtime/trace.StartRegion or runtime/trace.WithRegion. + Type string +} + +// Log provides details about a Log event. +type Log struct { + // Task is the ID of the task this region is associated with. + Task TaskID + + // Category is the category that was passed to runtime/trace.Log or runtime/trace.Logf. + Category string + + // Message is the message that was passed to runtime/trace.Log or runtime/trace.Logf. + Message string +} + +// Stack represents a stack. It's really a handle to a stack and it's trivially comparable. +// +// If two Stacks are equal then their Frames are guaranteed to be identical. If they are not +// equal, however, their Frames may still be equal. +type Stack struct { + table *evTable + id stackID +} + +// Frames is an iterator over the frames in a Stack. +func (s Stack) Frames(yield func(f StackFrame) bool) bool { + if s.id == 0 { + return true + } + stk := s.table.stacks.mustGet(s.id) + for _, pc := range stk.pcs { + f := s.table.pcs[pc] + sf := StackFrame{ + PC: f.pc, + Func: s.table.strings.mustGet(f.funcID), + File: s.table.strings.mustGet(f.fileID), + Line: f.line, + } + if !yield(sf) { + return false + } + } + return true +} + +// NoStack is a sentinel value that can be compared against any Stack value, indicating +// a lack of a stack trace. +var NoStack = Stack{} + +// StackFrame represents a single frame of a stack. +type StackFrame struct { + // PC is the program counter of the function call if this + // is not a leaf frame. If it's a leaf frame, it's the point + // at which the stack trace was taken. + PC uint64 + + // Func is the name of the function this frame maps to. + Func string + + // File is the file which contains the source code of Func. + File string + + // Line is the line number within File which maps to PC. + Line uint64 +} + +// ExperimentalEvent presents a raw view of an experimental event's arguments and thier names. +type ExperimentalEvent struct { + // Name is the name of the event. + Name string + + // ArgNames is the names of the event's arguments in order. + // This may refer to a globally shared slice. Copy before mutating. + ArgNames []string + + // Args contains the event's arguments. + Args []uint64 + + // Data is additional unparsed data that is associated with the experimental event. + // Data is likely to be shared across many ExperimentalEvents, so callers that parse + // Data are encouraged to cache the parse result and look it up by the value of Data. + Data *ExperimentalData +} + +// ExperimentalData represents some raw and unparsed sidecar data present in the trace that is +// associated with certain kinds of experimental events. For example, this data may contain +// tables needed to interpret ExperimentalEvent arguments, or the ExperimentEvent could just be +// a placeholder for a differently encoded event that's actually present in the experimental data. +type ExperimentalData struct { + // Batches contain the actual experimental data, along with metadata about each batch. + Batches []ExperimentalBatch +} + +// ExperimentalBatch represents a packet of unparsed data along with metadata about that packet. +type ExperimentalBatch struct { + // Thread is the ID of the thread that produced a packet of data. + Thread ThreadID + + // Data is a packet of unparsed data all produced by one thread. + Data []byte +} + +// Event represents a single event in the trace. +type Event struct { + table *evTable + ctx schedCtx + base baseEvent +} + +// Kind returns the kind of event that this is. +func (e Event) Kind() EventKind { + return go122Type2Kind[e.base.typ] +} + +// Time returns the timestamp of the event. +func (e Event) Time() Time { + return e.base.time +} + +// Goroutine returns the ID of the goroutine that was executing when +// this event happened. It describes part of the execution context +// for this event. +// +// Note that for goroutine state transitions this always refers to the +// state before the transition. For example, if a goroutine is just +// starting to run on this thread and/or proc, then this will return +// NoGoroutine. In this case, the goroutine starting to run will be +// can be found at Event.StateTransition().Resource. +func (e Event) Goroutine() GoID { + return e.ctx.G +} + +// Proc returns the ID of the proc this event event pertains to. +// +// Note that for proc state transitions this always refers to the +// state before the transition. For example, if a proc is just +// starting to run on this thread, then this will return NoProc. +func (e Event) Proc() ProcID { + return e.ctx.P +} + +// Thread returns the ID of the thread this event pertains to. +// +// Note that for thread state transitions this always refers to the +// state before the transition. For example, if a thread is just +// starting to run, then this will return NoThread. +// +// Note: tracking thread state is not currently supported, so this +// will always return a valid thread ID. However thread state transitions +// may be tracked in the future, and callers must be robust to this +// possibility. +func (e Event) Thread() ThreadID { + return e.ctx.M +} + +// Stack returns a handle to a stack associated with the event. +// +// This represents a stack trace at the current moment in time for +// the current execution context. +func (e Event) Stack() Stack { + if e.base.typ == evSync { + return NoStack + } + if e.base.typ == go122.EvCPUSample { + return Stack{table: e.table, id: stackID(e.base.args[0])} + } + spec := go122.Specs()[e.base.typ] + if len(spec.StackIDs) == 0 { + return NoStack + } + // The stack for the main execution context is always the + // first stack listed in StackIDs. Subtract one from this + // because we've peeled away the timestamp argument. + id := stackID(e.base.args[spec.StackIDs[0]-1]) + if id == 0 { + return NoStack + } + return Stack{table: e.table, id: id} +} + +// Metric returns details about a Metric event. +// +// Panics if Kind != EventMetric. +func (e Event) Metric() Metric { + if e.Kind() != EventMetric { + panic("Metric called on non-Metric event") + } + var m Metric + switch e.base.typ { + case go122.EvProcsChange: + m.Name = "/sched/gomaxprocs:threads" + m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} + case go122.EvHeapAlloc: + m.Name = "/memory/classes/heap/objects:bytes" + m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} + case go122.EvHeapGoal: + m.Name = "/gc/heap/goal:bytes" + m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} + default: + panic(fmt.Sprintf("internal error: unexpected event type for Metric kind: %s", go122.EventString(e.base.typ))) + } + return m +} + +// Label returns details about a Label event. +// +// Panics if Kind != EventLabel. +func (e Event) Label() Label { + if e.Kind() != EventLabel { + panic("Label called on non-Label event") + } + if e.base.typ != go122.EvGoLabel { + panic(fmt.Sprintf("internal error: unexpected event type for Label kind: %s", go122.EventString(e.base.typ))) + } + return Label{ + Label: e.table.strings.mustGet(stringID(e.base.args[0])), + Resource: ResourceID{Kind: ResourceGoroutine, id: int64(e.ctx.G)}, + } +} + +// Range returns details about an EventRangeBegin, EventRangeActive, or EventRangeEnd event. +// +// Panics if Kind != EventRangeBegin, Kind != EventRangeActive, and Kind != EventRangeEnd. +func (e Event) Range() Range { + if kind := e.Kind(); kind != EventRangeBegin && kind != EventRangeActive && kind != EventRangeEnd { + panic("Range called on non-Range event") + } + var r Range + switch e.base.typ { + case go122.EvSTWBegin, go122.EvSTWEnd: + // N.B. ordering.advance smuggles in the STW reason as e.base.args[0] + // for go122.EvSTWEnd (it's already there for Begin). + r.Name = "stop-the-world (" + e.table.strings.mustGet(stringID(e.base.args[0])) + ")" + r.Scope = ResourceID{Kind: ResourceGoroutine, id: int64(e.Goroutine())} + case go122.EvGCBegin, go122.EvGCActive, go122.EvGCEnd: + r.Name = "GC concurrent mark phase" + r.Scope = ResourceID{Kind: ResourceNone} + case go122.EvGCSweepBegin, go122.EvGCSweepActive, go122.EvGCSweepEnd: + r.Name = "GC incremental sweep" + r.Scope = ResourceID{Kind: ResourceProc} + if e.base.typ == go122.EvGCSweepActive { + r.Scope.id = int64(e.base.args[0]) + } else { + r.Scope.id = int64(e.Proc()) + } + r.Scope.id = int64(e.Proc()) + case go122.EvGCMarkAssistBegin, go122.EvGCMarkAssistActive, go122.EvGCMarkAssistEnd: + r.Name = "GC mark assist" + r.Scope = ResourceID{Kind: ResourceGoroutine} + if e.base.typ == go122.EvGCMarkAssistActive { + r.Scope.id = int64(e.base.args[0]) + } else { + r.Scope.id = int64(e.Goroutine()) + } + default: + panic(fmt.Sprintf("internal error: unexpected event type for Range kind: %s", go122.EventString(e.base.typ))) + } + return r +} + +// RangeAttributes returns attributes for a completed range. +// +// Panics if Kind != EventRangeEnd. +func (e Event) RangeAttributes() []RangeAttribute { + if e.Kind() != EventRangeEnd { + panic("Range called on non-Range event") + } + if e.base.typ != go122.EvGCSweepEnd { + return nil + } + return []RangeAttribute{ + { + Name: "bytes swept", + Value: Value{kind: ValueUint64, scalar: e.base.args[0]}, + }, + { + Name: "bytes reclaimed", + Value: Value{kind: ValueUint64, scalar: e.base.args[1]}, + }, + } +} + +// Task returns details about a TaskBegin or TaskEnd event. +// +// Panics if Kind != EventTaskBegin and Kind != EventTaskEnd. +func (e Event) Task() Task { + if kind := e.Kind(); kind != EventTaskBegin && kind != EventTaskEnd { + panic("Task called on non-Task event") + } + parentID := NoTask + var typ string + switch e.base.typ { + case go122.EvUserTaskBegin: + parentID = TaskID(e.base.args[1]) + typ = e.table.strings.mustGet(stringID(e.base.args[2])) + case go122.EvUserTaskEnd: + parentID = TaskID(e.base.extra(version.Go122)[0]) + typ = e.table.getExtraString(extraStringID(e.base.extra(version.Go122)[1])) + default: + panic(fmt.Sprintf("internal error: unexpected event type for Task kind: %s", go122.EventString(e.base.typ))) + } + return Task{ + ID: TaskID(e.base.args[0]), + Parent: parentID, + Type: typ, + } +} + +// Region returns details about a RegionBegin or RegionEnd event. +// +// Panics if Kind != EventRegionBegin and Kind != EventRegionEnd. +func (e Event) Region() Region { + if kind := e.Kind(); kind != EventRegionBegin && kind != EventRegionEnd { + panic("Region called on non-Region event") + } + if e.base.typ != go122.EvUserRegionBegin && e.base.typ != go122.EvUserRegionEnd { + panic(fmt.Sprintf("internal error: unexpected event type for Region kind: %s", go122.EventString(e.base.typ))) + } + return Region{ + Task: TaskID(e.base.args[0]), + Type: e.table.strings.mustGet(stringID(e.base.args[1])), + } +} + +// Log returns details about a Log event. +// +// Panics if Kind != EventLog. +func (e Event) Log() Log { + if e.Kind() != EventLog { + panic("Log called on non-Log event") + } + if e.base.typ != go122.EvUserLog { + panic(fmt.Sprintf("internal error: unexpected event type for Log kind: %s", go122.EventString(e.base.typ))) + } + return Log{ + Task: TaskID(e.base.args[0]), + Category: e.table.strings.mustGet(stringID(e.base.args[1])), + Message: e.table.strings.mustGet(stringID(e.base.args[2])), + } +} + +// StateTransition returns details about a StateTransition event. +// +// Panics if Kind != EventStateTransition. +func (e Event) StateTransition() StateTransition { + if e.Kind() != EventStateTransition { + panic("StateTransition called on non-StateTransition event") + } + var s StateTransition + switch e.base.typ { + case go122.EvProcStart: + s = procStateTransition(ProcID(e.base.args[0]), ProcIdle, ProcRunning) + case go122.EvProcStop: + s = procStateTransition(e.ctx.P, ProcRunning, ProcIdle) + case go122.EvProcSteal: + // N.B. ordering.advance populates e.base.extra. + beforeState := ProcRunning + if go122.ProcStatus(e.base.extra(version.Go122)[0]) == go122.ProcSyscallAbandoned { + // We've lost information because this ProcSteal advanced on a + // SyscallAbandoned state. Treat the P as idle because ProcStatus + // treats SyscallAbandoned as Idle. Otherwise we'll have an invalid + // transition. + beforeState = ProcIdle + } + s = procStateTransition(ProcID(e.base.args[0]), beforeState, ProcIdle) + case go122.EvProcStatus: + // N.B. ordering.advance populates e.base.extra. + s = procStateTransition(ProcID(e.base.args[0]), ProcState(e.base.extra(version.Go122)[0]), go122ProcStatus2ProcState[e.base.args[1]]) + case go122.EvGoCreate, go122.EvGoCreateBlocked: + status := GoRunnable + if e.base.typ == go122.EvGoCreateBlocked { + status = GoWaiting + } + s = goStateTransition(GoID(e.base.args[0]), GoNotExist, status) + s.Stack = Stack{table: e.table, id: stackID(e.base.args[1])} + case go122.EvGoCreateSyscall: + s = goStateTransition(GoID(e.base.args[0]), GoNotExist, GoSyscall) + case go122.EvGoStart: + s = goStateTransition(GoID(e.base.args[0]), GoRunnable, GoRunning) + case go122.EvGoDestroy: + s = goStateTransition(e.ctx.G, GoRunning, GoNotExist) + s.Stack = e.Stack() // This event references the resource the event happened on. + case go122.EvGoDestroySyscall: + s = goStateTransition(e.ctx.G, GoSyscall, GoNotExist) + case go122.EvGoStop: + s = goStateTransition(e.ctx.G, GoRunning, GoRunnable) + s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) + s.Stack = e.Stack() // This event references the resource the event happened on. + case go122.EvGoBlock: + s = goStateTransition(e.ctx.G, GoRunning, GoWaiting) + s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) + s.Stack = e.Stack() // This event references the resource the event happened on. + case go122.EvGoUnblock, go122.EvGoSwitch, go122.EvGoSwitchDestroy: + // N.B. GoSwitch and GoSwitchDestroy both emit additional events, but + // the first thing they both do is unblock the goroutine they name, + // identically to an unblock event (even their arguments match). + s = goStateTransition(GoID(e.base.args[0]), GoWaiting, GoRunnable) + case go122.EvGoSyscallBegin: + s = goStateTransition(e.ctx.G, GoRunning, GoSyscall) + s.Stack = e.Stack() // This event references the resource the event happened on. + case go122.EvGoSyscallEnd: + s = goStateTransition(e.ctx.G, GoSyscall, GoRunning) + s.Stack = e.Stack() // This event references the resource the event happened on. + case go122.EvGoSyscallEndBlocked: + s = goStateTransition(e.ctx.G, GoSyscall, GoRunnable) + s.Stack = e.Stack() // This event references the resource the event happened on. + case go122.EvGoStatus, go122.EvGoStatusStack: + // N.B. ordering.advance populates e.base.extra. + s = goStateTransition(GoID(e.base.args[0]), GoState(e.base.extra(version.Go122)[0]), go122GoStatus2GoState[e.base.args[2]]) + default: + panic(fmt.Sprintf("internal error: unexpected event type for StateTransition kind: %s", go122.EventString(e.base.typ))) + } + return s +} + +// Experimental returns a view of the raw event for an experimental event. +// +// Panics if Kind != EventExperimental. +func (e Event) Experimental() ExperimentalEvent { + if e.Kind() != EventExperimental { + panic("Experimental called on non-Experimental event") + } + spec := go122.Specs()[e.base.typ] + argNames := spec.Args[1:] + return ExperimentalEvent{ + Name: spec.Name, + ArgNames: argNames, // Skip timestamp; already handled. + Args: e.base.args[1 : 1+len(argNames)], + Data: e.table.expData[spec.Experiment], + } +} + +const evSync = ^event.Type(0) + +var go122Type2Kind = [...]EventKind{ + go122.EvCPUSample: EventStackSample, + go122.EvProcsChange: EventMetric, + go122.EvProcStart: EventStateTransition, + go122.EvProcStop: EventStateTransition, + go122.EvProcSteal: EventStateTransition, + go122.EvProcStatus: EventStateTransition, + go122.EvGoCreate: EventStateTransition, + go122.EvGoCreateSyscall: EventStateTransition, + go122.EvGoStart: EventStateTransition, + go122.EvGoDestroy: EventStateTransition, + go122.EvGoDestroySyscall: EventStateTransition, + go122.EvGoStop: EventStateTransition, + go122.EvGoBlock: EventStateTransition, + go122.EvGoUnblock: EventStateTransition, + go122.EvGoSyscallBegin: EventStateTransition, + go122.EvGoSyscallEnd: EventStateTransition, + go122.EvGoSyscallEndBlocked: EventStateTransition, + go122.EvGoStatus: EventStateTransition, + go122.EvSTWBegin: EventRangeBegin, + go122.EvSTWEnd: EventRangeEnd, + go122.EvGCActive: EventRangeActive, + go122.EvGCBegin: EventRangeBegin, + go122.EvGCEnd: EventRangeEnd, + go122.EvGCSweepActive: EventRangeActive, + go122.EvGCSweepBegin: EventRangeBegin, + go122.EvGCSweepEnd: EventRangeEnd, + go122.EvGCMarkAssistActive: EventRangeActive, + go122.EvGCMarkAssistBegin: EventRangeBegin, + go122.EvGCMarkAssistEnd: EventRangeEnd, + go122.EvHeapAlloc: EventMetric, + go122.EvHeapGoal: EventMetric, + go122.EvGoLabel: EventLabel, + go122.EvUserTaskBegin: EventTaskBegin, + go122.EvUserTaskEnd: EventTaskEnd, + go122.EvUserRegionBegin: EventRegionBegin, + go122.EvUserRegionEnd: EventRegionEnd, + go122.EvUserLog: EventLog, + go122.EvGoSwitch: EventStateTransition, + go122.EvGoSwitchDestroy: EventStateTransition, + go122.EvGoCreateBlocked: EventStateTransition, + go122.EvGoStatusStack: EventStateTransition, + go122.EvSpan: EventExperimental, + go122.EvSpanAlloc: EventExperimental, + go122.EvSpanFree: EventExperimental, + go122.EvHeapObject: EventExperimental, + go122.EvHeapObjectAlloc: EventExperimental, + go122.EvHeapObjectFree: EventExperimental, + go122.EvGoroutineStack: EventExperimental, + go122.EvGoroutineStackAlloc: EventExperimental, + go122.EvGoroutineStackFree: EventExperimental, + evSync: EventSync, +} + +var go122GoStatus2GoState = [...]GoState{ + go122.GoRunnable: GoRunnable, + go122.GoRunning: GoRunning, + go122.GoWaiting: GoWaiting, + go122.GoSyscall: GoSyscall, +} + +var go122ProcStatus2ProcState = [...]ProcState{ + go122.ProcRunning: ProcRunning, + go122.ProcIdle: ProcIdle, + go122.ProcSyscall: ProcRunning, + go122.ProcSyscallAbandoned: ProcIdle, +} + +// String returns the event as a human-readable string. +// +// The format of the string is intended for debugging and is subject to change. +func (e Event) String() string { + var sb strings.Builder + fmt.Fprintf(&sb, "M=%d P=%d G=%d", e.Thread(), e.Proc(), e.Goroutine()) + fmt.Fprintf(&sb, " %s Time=%d", e.Kind(), e.Time()) + // Kind-specific fields. + switch kind := e.Kind(); kind { + case EventMetric: + m := e.Metric() + fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, valueAsString(m.Value)) + case EventLabel: + l := e.Label() + fmt.Fprintf(&sb, " Label=%q Resource=%s", l.Label, l.Resource) + case EventRangeBegin, EventRangeActive, EventRangeEnd: + r := e.Range() + fmt.Fprintf(&sb, " Name=%q Scope=%s", r.Name, r.Scope) + if kind == EventRangeEnd { + fmt.Fprintf(&sb, " Attributes=[") + for i, attr := range e.RangeAttributes() { + if i != 0 { + fmt.Fprintf(&sb, " ") + } + fmt.Fprintf(&sb, "%q=%s", attr.Name, valueAsString(attr.Value)) + } + fmt.Fprintf(&sb, "]") + } + case EventTaskBegin, EventTaskEnd: + t := e.Task() + fmt.Fprintf(&sb, " ID=%d Parent=%d Type=%q", t.ID, t.Parent, t.Type) + case EventRegionBegin, EventRegionEnd: + r := e.Region() + fmt.Fprintf(&sb, " Task=%d Type=%q", r.Task, r.Type) + case EventLog: + l := e.Log() + fmt.Fprintf(&sb, " Task=%d Category=%q Message=%q", l.Task, l.Category, l.Message) + case EventStateTransition: + s := e.StateTransition() + fmt.Fprintf(&sb, " Resource=%s Reason=%q", s.Resource, s.Reason) + switch s.Resource.Kind { + case ResourceGoroutine: + id := s.Resource.Goroutine() + old, new := s.Goroutine() + fmt.Fprintf(&sb, " GoID=%d %s->%s", id, old, new) + case ResourceProc: + id := s.Resource.Proc() + old, new := s.Proc() + fmt.Fprintf(&sb, " ProcID=%d %s->%s", id, old, new) + } + if s.Stack != NoStack { + fmt.Fprintln(&sb) + fmt.Fprintln(&sb, "TransitionStack=") + s.Stack.Frames(func(f StackFrame) bool { + fmt.Fprintf(&sb, "\t%s @ 0x%x\n", f.Func, f.PC) + fmt.Fprintf(&sb, "\t\t%s:%d\n", f.File, f.Line) + return true + }) + } + case EventExperimental: + r := e.Experimental() + fmt.Fprintf(&sb, " Name=%s ArgNames=%v Args=%v", r.Name, r.ArgNames, r.Args) + } + if stk := e.Stack(); stk != NoStack { + fmt.Fprintln(&sb) + fmt.Fprintln(&sb, "Stack=") + stk.Frames(func(f StackFrame) bool { + fmt.Fprintf(&sb, "\t%s @ 0x%x\n", f.Func, f.PC) + fmt.Fprintf(&sb, "\t\t%s:%d\n", f.File, f.Line) + return true + }) + } + return sb.String() +} + +// validateTableIDs checks to make sure lookups in e.table +// will work. +func (e Event) validateTableIDs() error { + if e.base.typ == evSync { + return nil + } + spec := go122.Specs()[e.base.typ] + + // Check stacks. + for _, i := range spec.StackIDs { + id := stackID(e.base.args[i-1]) + _, ok := e.table.stacks.get(id) + if !ok { + return fmt.Errorf("found invalid stack ID %d for event %s", id, spec.Name) + } + } + // N.B. Strings referenced by stack frames are validated + // early on, when reading the stacks in to begin with. + + // Check strings. + for _, i := range spec.StringIDs { + id := stringID(e.base.args[i-1]) + _, ok := e.table.strings.get(id) + if !ok { + return fmt.Errorf("found invalid string ID %d for event %s", id, spec.Name) + } + } + return nil +} + +func syncEvent(table *evTable, ts Time) Event { + return Event{ + table: table, + ctx: schedCtx{ + G: NoGoroutine, + P: NoProc, + M: NoThread, + }, + base: baseEvent{ + typ: evSync, + time: ts, + }, + } +} diff --git a/src/internal/trace/event/event.go b/src/internal/trace/event/event.go new file mode 100644 index 0000000000..adcb8811d8 --- /dev/null +++ b/src/internal/trace/event/event.go @@ -0,0 +1,102 @@ +// 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. + +package event + +// Type is the common in-memory representation of the low-leve +type Type uint8 + +// Spec is a specification for a trace event. It contains sufficient information +// to perform basic parsing of any trace event for any version of Go. +type Spec struct { + // Name is the human-readable name of the trace event. + Name string + + // Args contains the names of each trace event's argument. + // Its length determines the number of arguments an event has. + // + // Argument names follow a certain structure and this structure + // is relied on by the testing framework to type-check arguments. + // The structure is is: + // + // (?P[A-Za-z]+_)?(?P[A-Za-z]+) + // + // In sum, it's an optional name followed by a type. If the name + // is present, it is separated from the type with an underscore. + // The valid argument types and the Go types they map to are listed + // in the ArgTypes variable. + Args []string + + // StringIDs indicates which of the arguments are string IDs. + StringIDs []int + + // StackIDs indicates which of the arguments are stack IDs. + // + // The list is not sorted. The first index always refers to + // the main stack for the current execution context of the event. + StackIDs []int + + // StartEv indicates the event type of the corresponding "start" + // event, if this event is an "end," for a pair of events that + // represent a time range. + StartEv Type + + // IsTimedEvent indicates whether this is an event that both + // appears in the main event stream and is surfaced to the + // trace reader. + // + // Events that are not "timed" are considered "structural" + // since they either need significant reinterpretation or + // otherwise aren't actually surfaced by the trace reader. + IsTimedEvent bool + + // HasData is true if the event has trailer consisting of a + // varint length followed by unencoded bytes of some data. + // + // An event may not be both a timed event and have data. + HasData bool + + // IsStack indicates that the event represents a complete + // stack trace. Specifically, it means that after the arguments + // there's a varint length, followed by 4*length varints. Each + // group of 4 represents the PC, file ID, func ID, and line number + // in that order. + IsStack bool + + // Experiment indicates the ID of an experiment this event is associated + // with. If Experiment is not NoExperiment, then the event is experimental + // and will be exposed as an EventExperiment. + Experiment Experiment +} + +// ArgTypes is a list of valid argument types for use in Args. +// +// See the documentation of Args for more details. +var ArgTypes = [...]string{ + "seq", // sequence number + "pstatus", // P status + "gstatus", // G status + "g", // trace.GoID + "m", // trace.ThreadID + "p", // trace.ProcID + "string", // string ID + "stack", // stack ID + "value", // uint64 + "task", // trace.TaskID +} + +// Names is a helper that produces a mapping of event names to event types. +func Names(specs []Spec) map[string]Type { + nameToType := make(map[string]Type) + for i, spec := range specs { + nameToType[spec.Name] = Type(byte(i)) + } + return nameToType +} + +// Experiment is an experiment ID that events may be associated with. +type Experiment uint + +// NoExperiment is the reserved ID 0 indicating no experiment. +const NoExperiment Experiment = 0 diff --git a/src/internal/trace/event/go122/event.go b/src/internal/trace/event/go122/event.go new file mode 100644 index 0000000000..f6075e3ed5 --- /dev/null +++ b/src/internal/trace/event/go122/event.go @@ -0,0 +1,511 @@ +// 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. + +package go122 + +import ( + "fmt" + "internal/trace/event" +) + +const ( + EvNone event.Type = iota // unused + + // Structural events. + EvEventBatch // start of per-M batch of events [generation, M ID, timestamp, batch length] + EvStacks // start of a section of the stack table [...EvStack] + EvStack // stack table entry [ID, ...{PC, func string ID, file string ID, line #}] + EvStrings // start of a section of the string dictionary [...EvString] + EvString // string dictionary entry [ID, length, string] + EvCPUSamples // start of a section of CPU samples [...EvCPUSample] + EvCPUSample // CPU profiling sample [timestamp, M ID, P ID, goroutine ID, stack ID] + EvFrequency // timestamp units per sec [freq] + + // Procs. + EvProcsChange // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack ID] + EvProcStart // start of P [timestamp, P ID, P seq] + EvProcStop // stop of P [timestamp] + EvProcSteal // P was stolen [timestamp, P ID, P seq, M ID] + EvProcStatus // P status at the start of a generation [timestamp, P ID, status] + + // Goroutines. + EvGoCreate // goroutine creation [timestamp, new goroutine ID, new stack ID, stack ID] + EvGoCreateSyscall // goroutine appears in syscall (cgo callback) [timestamp, new goroutine ID] + EvGoStart // goroutine starts running [timestamp, goroutine ID, goroutine seq] + EvGoDestroy // goroutine ends [timestamp] + EvGoDestroySyscall // goroutine ends in syscall (cgo callback) [timestamp] + EvGoStop // goroutine yields its time, but is runnable [timestamp, reason, stack ID] + EvGoBlock // goroutine blocks [timestamp, reason, stack ID] + EvGoUnblock // goroutine is unblocked [timestamp, goroutine ID, goroutine seq, stack ID] + EvGoSyscallBegin // syscall enter [timestamp, P seq, stack ID] + EvGoSyscallEnd // syscall exit [timestamp] + EvGoSyscallEndBlocked // syscall exit and it blocked at some point [timestamp] + EvGoStatus // goroutine status at the start of a generation [timestamp, goroutine ID, thread ID, status] + + // STW. + EvSTWBegin // STW start [timestamp, kind] + EvSTWEnd // STW done [timestamp] + + // GC events. + EvGCActive // GC active [timestamp, seq] + EvGCBegin // GC start [timestamp, seq, stack ID] + EvGCEnd // GC done [timestamp, seq] + EvGCSweepActive // GC sweep active [timestamp, P ID] + EvGCSweepBegin // GC sweep start [timestamp, stack ID] + EvGCSweepEnd // GC sweep done [timestamp, swept bytes, reclaimed bytes] + EvGCMarkAssistActive // GC mark assist active [timestamp, goroutine ID] + EvGCMarkAssistBegin // GC mark assist start [timestamp, stack ID] + EvGCMarkAssistEnd // GC mark assist done [timestamp] + EvHeapAlloc // gcController.heapLive change [timestamp, heap alloc in bytes] + EvHeapGoal // gcController.heapGoal() change [timestamp, heap goal in bytes] + + // Annotations. + EvGoLabel // apply string label to current running goroutine [timestamp, label string ID] + EvUserTaskBegin // trace.NewTask [timestamp, internal task ID, internal parent task ID, name string ID, stack ID] + EvUserTaskEnd // end of a task [timestamp, internal task ID, stack ID] + EvUserRegionBegin // trace.{Start,With}Region [timestamp, internal task ID, name string ID, stack ID] + EvUserRegionEnd // trace.{End,With}Region [timestamp, internal task ID, name string ID, stack ID] + EvUserLog // trace.Log [timestamp, internal task ID, key string ID, value string ID, stack] + + // Coroutines. Added in Go 1.23. + EvGoSwitch // goroutine switch (coroswitch) [timestamp, goroutine ID, goroutine seq] + EvGoSwitchDestroy // goroutine switch and destroy [timestamp, goroutine ID, goroutine seq] + EvGoCreateBlocked // goroutine creation (starts blocked) [timestamp, new goroutine ID, new stack ID, stack ID] + + // GoStatus with stack. Added in Go 1.23. + EvGoStatusStack // goroutine status at the start of a generation, with a stack [timestamp, goroutine ID, M ID, status, stack ID] + + // Batch event for an experimental batch with a custom format. Added in Go 1.23. + EvExperimentalBatch // start of extra data [experiment ID, generation, M ID, timestamp, batch length, batch data...] +) + +// Experiments. +const ( + // AllocFree is the alloc-free events experiment. + AllocFree event.Experiment = 1 + iota +) + +// Experimental events. +const ( + _ event.Type = 127 + iota + + // Experimental events for AllocFree. + + // Experimental heap span events. Added in Go 1.23. + EvSpan // heap span exists [timestamp, id, npages, type/class] + EvSpanAlloc // heap span alloc [timestamp, id, npages, type/class] + EvSpanFree // heap span free [timestamp, id] + + // Experimental heap object events. Added in Go 1.23. + EvHeapObject // heap object exists [timestamp, id, type] + EvHeapObjectAlloc // heap object alloc [timestamp, id, type] + EvHeapObjectFree // heap object free [timestamp, id] + + // Experimental goroutine stack events. Added in Go 1.23. + EvGoroutineStack // stack exists [timestamp, id, order] + EvGoroutineStackAlloc // stack alloc [timestamp, id, order] + EvGoroutineStackFree // stack free [timestamp, id] +) + +// EventString returns the name of a Go 1.22 event. +func EventString(typ event.Type) string { + if int(typ) < len(specs) { + return specs[typ].Name + } + return fmt.Sprintf("Invalid(%d)", typ) +} + +func Specs() []event.Spec { + return specs[:] +} + +var specs = [...]event.Spec{ + // "Structural" Events. + EvEventBatch: event.Spec{ + Name: "EventBatch", + Args: []string{"gen", "m", "time", "size"}, + }, + EvStacks: event.Spec{ + Name: "Stacks", + }, + EvStack: event.Spec{ + Name: "Stack", + Args: []string{"id", "nframes"}, + IsStack: true, + }, + EvStrings: event.Spec{ + Name: "Strings", + }, + EvString: event.Spec{ + Name: "String", + Args: []string{"id"}, + HasData: true, + }, + EvCPUSamples: event.Spec{ + Name: "CPUSamples", + }, + EvCPUSample: event.Spec{ + Name: "CPUSample", + Args: []string{"time", "m", "p", "g", "stack"}, + // N.B. There's clearly a timestamp here, but these Events + // are special in that they don't appear in the regular + // M streams. + }, + EvFrequency: event.Spec{ + Name: "Frequency", + Args: []string{"freq"}, + }, + EvExperimentalBatch: event.Spec{ + Name: "ExperimentalBatch", + Args: []string{"exp", "gen", "m", "time"}, + HasData: true, // Easier to represent for raw readers. + }, + + // "Timed" Events. + EvProcsChange: event.Spec{ + Name: "ProcsChange", + Args: []string{"dt", "procs_value", "stack"}, + IsTimedEvent: true, + StackIDs: []int{2}, + }, + EvProcStart: event.Spec{ + Name: "ProcStart", + Args: []string{"dt", "p", "p_seq"}, + IsTimedEvent: true, + }, + EvProcStop: event.Spec{ + Name: "ProcStop", + Args: []string{"dt"}, + IsTimedEvent: true, + }, + EvProcSteal: event.Spec{ + Name: "ProcSteal", + Args: []string{"dt", "p", "p_seq", "m"}, + IsTimedEvent: true, + }, + EvProcStatus: event.Spec{ + Name: "ProcStatus", + Args: []string{"dt", "p", "pstatus"}, + IsTimedEvent: true, + }, + EvGoCreate: event.Spec{ + Name: "GoCreate", + Args: []string{"dt", "new_g", "new_stack", "stack"}, + IsTimedEvent: true, + StackIDs: []int{3, 2}, + }, + EvGoCreateSyscall: event.Spec{ + Name: "GoCreateSyscall", + Args: []string{"dt", "new_g"}, + IsTimedEvent: true, + }, + EvGoStart: event.Spec{ + Name: "GoStart", + Args: []string{"dt", "g", "g_seq"}, + IsTimedEvent: true, + }, + EvGoDestroy: event.Spec{ + Name: "GoDestroy", + Args: []string{"dt"}, + IsTimedEvent: true, + }, + EvGoDestroySyscall: event.Spec{ + Name: "GoDestroySyscall", + Args: []string{"dt"}, + IsTimedEvent: true, + }, + EvGoStop: event.Spec{ + Name: "GoStop", + Args: []string{"dt", "reason_string", "stack"}, + IsTimedEvent: true, + StackIDs: []int{2}, + StringIDs: []int{1}, + }, + EvGoBlock: event.Spec{ + Name: "GoBlock", + Args: []string{"dt", "reason_string", "stack"}, + IsTimedEvent: true, + StackIDs: []int{2}, + StringIDs: []int{1}, + }, + EvGoUnblock: event.Spec{ + Name: "GoUnblock", + Args: []string{"dt", "g", "g_seq", "stack"}, + IsTimedEvent: true, + StackIDs: []int{3}, + }, + EvGoSyscallBegin: event.Spec{ + Name: "GoSyscallBegin", + Args: []string{"dt", "p_seq", "stack"}, + IsTimedEvent: true, + StackIDs: []int{2}, + }, + EvGoSyscallEnd: event.Spec{ + Name: "GoSyscallEnd", + Args: []string{"dt"}, + StartEv: EvGoSyscallBegin, + IsTimedEvent: true, + }, + EvGoSyscallEndBlocked: event.Spec{ + Name: "GoSyscallEndBlocked", + Args: []string{"dt"}, + StartEv: EvGoSyscallBegin, + IsTimedEvent: true, + }, + EvGoStatus: event.Spec{ + Name: "GoStatus", + Args: []string{"dt", "g", "m", "gstatus"}, + IsTimedEvent: true, + }, + EvSTWBegin: event.Spec{ + Name: "STWBegin", + Args: []string{"dt", "kind_string", "stack"}, + IsTimedEvent: true, + StackIDs: []int{2}, + StringIDs: []int{1}, + }, + EvSTWEnd: event.Spec{ + Name: "STWEnd", + Args: []string{"dt"}, + StartEv: EvSTWBegin, + IsTimedEvent: true, + }, + EvGCActive: event.Spec{ + Name: "GCActive", + Args: []string{"dt", "gc_seq"}, + IsTimedEvent: true, + StartEv: EvGCBegin, + }, + EvGCBegin: event.Spec{ + Name: "GCBegin", + Args: []string{"dt", "gc_seq", "stack"}, + IsTimedEvent: true, + StackIDs: []int{2}, + }, + EvGCEnd: event.Spec{ + Name: "GCEnd", + Args: []string{"dt", "gc_seq"}, + StartEv: EvGCBegin, + IsTimedEvent: true, + }, + EvGCSweepActive: event.Spec{ + Name: "GCSweepActive", + Args: []string{"dt", "p"}, + StartEv: EvGCSweepBegin, + IsTimedEvent: true, + }, + EvGCSweepBegin: event.Spec{ + Name: "GCSweepBegin", + Args: []string{"dt", "stack"}, + IsTimedEvent: true, + StackIDs: []int{1}, + }, + EvGCSweepEnd: event.Spec{ + Name: "GCSweepEnd", + Args: []string{"dt", "swept_value", "reclaimed_value"}, + StartEv: EvGCSweepBegin, + IsTimedEvent: true, + }, + EvGCMarkAssistActive: event.Spec{ + Name: "GCMarkAssistActive", + Args: []string{"dt", "g"}, + StartEv: EvGCMarkAssistBegin, + IsTimedEvent: true, + }, + EvGCMarkAssistBegin: event.Spec{ + Name: "GCMarkAssistBegin", + Args: []string{"dt", "stack"}, + IsTimedEvent: true, + StackIDs: []int{1}, + }, + EvGCMarkAssistEnd: event.Spec{ + Name: "GCMarkAssistEnd", + Args: []string{"dt"}, + StartEv: EvGCMarkAssistBegin, + IsTimedEvent: true, + }, + EvHeapAlloc: event.Spec{ + Name: "HeapAlloc", + Args: []string{"dt", "heapalloc_value"}, + IsTimedEvent: true, + }, + EvHeapGoal: event.Spec{ + Name: "HeapGoal", + Args: []string{"dt", "heapgoal_value"}, + IsTimedEvent: true, + }, + EvGoLabel: event.Spec{ + Name: "GoLabel", + Args: []string{"dt", "label_string"}, + IsTimedEvent: true, + StringIDs: []int{1}, + }, + EvUserTaskBegin: event.Spec{ + Name: "UserTaskBegin", + Args: []string{"dt", "task", "parent_task", "name_string", "stack"}, + IsTimedEvent: true, + StackIDs: []int{4}, + StringIDs: []int{3}, + }, + EvUserTaskEnd: event.Spec{ + Name: "UserTaskEnd", + Args: []string{"dt", "task", "stack"}, + IsTimedEvent: true, + StackIDs: []int{2}, + }, + EvUserRegionBegin: event.Spec{ + Name: "UserRegionBegin", + Args: []string{"dt", "task", "name_string", "stack"}, + IsTimedEvent: true, + StackIDs: []int{3}, + StringIDs: []int{2}, + }, + EvUserRegionEnd: event.Spec{ + Name: "UserRegionEnd", + Args: []string{"dt", "task", "name_string", "stack"}, + StartEv: EvUserRegionBegin, + IsTimedEvent: true, + StackIDs: []int{3}, + StringIDs: []int{2}, + }, + EvUserLog: event.Spec{ + Name: "UserLog", + Args: []string{"dt", "task", "key_string", "value_string", "stack"}, + IsTimedEvent: true, + StackIDs: []int{4}, + StringIDs: []int{2, 3}, + }, + EvGoSwitch: event.Spec{ + Name: "GoSwitch", + Args: []string{"dt", "g", "g_seq"}, + IsTimedEvent: true, + }, + EvGoSwitchDestroy: event.Spec{ + Name: "GoSwitchDestroy", + Args: []string{"dt", "g", "g_seq"}, + IsTimedEvent: true, + }, + EvGoCreateBlocked: event.Spec{ + Name: "GoCreateBlocked", + Args: []string{"dt", "new_g", "new_stack", "stack"}, + IsTimedEvent: true, + StackIDs: []int{3, 2}, + }, + EvGoStatusStack: event.Spec{ + Name: "GoStatusStack", + Args: []string{"dt", "g", "m", "gstatus", "stack"}, + IsTimedEvent: true, + StackIDs: []int{4}, + }, + + // Experimental events. + + EvSpan: event.Spec{ + Name: "Span", + Args: []string{"dt", "id", "npages_value", "kindclass"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvSpanAlloc: event.Spec{ + Name: "SpanAlloc", + Args: []string{"dt", "id", "npages_value", "kindclass"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvSpanFree: event.Spec{ + Name: "SpanFree", + Args: []string{"dt", "id"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvHeapObject: event.Spec{ + Name: "HeapObject", + Args: []string{"dt", "id", "type"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvHeapObjectAlloc: event.Spec{ + Name: "HeapObjectAlloc", + Args: []string{"dt", "id", "type"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvHeapObjectFree: event.Spec{ + Name: "HeapObjectFree", + Args: []string{"dt", "id"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvGoroutineStack: event.Spec{ + Name: "GoroutineStack", + Args: []string{"dt", "id", "order"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvGoroutineStackAlloc: event.Spec{ + Name: "GoroutineStackAlloc", + Args: []string{"dt", "id", "order"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, + EvGoroutineStackFree: event.Spec{ + Name: "GoroutineStackFree", + Args: []string{"dt", "id"}, + IsTimedEvent: true, + Experiment: AllocFree, + }, +} + +type GoStatus uint8 + +const ( + GoBad GoStatus = iota + GoRunnable + GoRunning + GoSyscall + GoWaiting +) + +func (s GoStatus) String() string { + switch s { + case GoRunnable: + return "Runnable" + case GoRunning: + return "Running" + case GoSyscall: + return "Syscall" + case GoWaiting: + return "Waiting" + } + return "Bad" +} + +type ProcStatus uint8 + +const ( + ProcBad ProcStatus = iota + ProcRunning + ProcIdle + ProcSyscall + ProcSyscallAbandoned +) + +func (s ProcStatus) String() string { + switch s { + case ProcRunning: + return "Running" + case ProcIdle: + return "Idle" + case ProcSyscall: + return "Syscall" + } + return "Bad" +} + +const ( + // Various format-specific constants. + MaxBatchSize = 64 << 10 + MaxFramesPerStack = 128 + MaxStringSize = 1 << 10 +) diff --git a/src/internal/trace/event/requirements.go b/src/internal/trace/event/requirements.go new file mode 100644 index 0000000000..c5adf2e0c2 --- /dev/null +++ b/src/internal/trace/event/requirements.go @@ -0,0 +1,26 @@ +// 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. + +package event + +// SchedReqs is a set of constraints on what the scheduling +// context must look like. +type SchedReqs struct { + Thread Constraint + Proc Constraint + Goroutine Constraint +} + +// Constraint represents a various presence requirements. +type Constraint uint8 + +const ( + MustNotHave Constraint = iota + MayHave + MustHave +) + +// UserGoReqs is a common requirement among events that are running +// or are close to running user code. +var UserGoReqs = SchedReqs{Thread: MustHave, Proc: MustHave, Goroutine: MustHave} diff --git a/src/internal/trace/event_test.go b/src/internal/trace/event_test.go new file mode 100644 index 0000000000..c81a45185d --- /dev/null +++ b/src/internal/trace/event_test.go @@ -0,0 +1,43 @@ +// 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. + +package trace + +import "testing" + +func TestPanicEvent(t *testing.T) { + // Use a sync event for this because it doesn't have any extra metadata. + ev := syncEvent(nil, 0) + + mustPanic(t, func() { + _ = ev.Range() + }) + mustPanic(t, func() { + _ = ev.Metric() + }) + mustPanic(t, func() { + _ = ev.Log() + }) + mustPanic(t, func() { + _ = ev.Task() + }) + mustPanic(t, func() { + _ = ev.Region() + }) + mustPanic(t, func() { + _ = ev.Label() + }) + mustPanic(t, func() { + _ = ev.RangeAttributes() + }) +} + +func mustPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r == nil { + t.Fatal("failed to panic") + } + }() + f() +} diff --git a/src/internal/trace/export_test.go b/src/internal/trace/export_test.go new file mode 100644 index 0000000000..4f494ee8cb --- /dev/null +++ b/src/internal/trace/export_test.go @@ -0,0 +1,7 @@ +// Copyright 2024 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 trace + +var BandsPerSeries = bandsPerSeries diff --git a/src/internal/trace/gc.go b/src/internal/trace/gc.go index 60c45f46aa..bf271ed73b 100644 --- a/src/internal/trace/gc.go +++ b/src/internal/trace/gc.go @@ -6,7 +6,6 @@ package trace import ( "container/heap" - tracev2 "internal/trace/v2" "math" "sort" "strings" @@ -53,7 +52,7 @@ const ( // // If the UtilPerProc flag is not given, this always returns a single // utilization function. Otherwise, it returns one function per P. -func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUtil { +func MutatorUtilizationV2(events []Event, flags UtilFlags) [][]MutatorUtil { // Set up a bunch of analysis state. type perP struct { // gc > 0 indicates that GC is active on this P. @@ -72,34 +71,34 @@ func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUt out := [][]MutatorUtil{} stw := 0 ps := []perP{} - inGC := make(map[tracev2.GoID]bool) - states := make(map[tracev2.GoID]tracev2.GoState) - bgMark := make(map[tracev2.GoID]bool) + inGC := make(map[GoID]bool) + states := make(map[GoID]GoState) + bgMark := make(map[GoID]bool) procs := []procsCount{} seenSync := false // Helpers. - handleSTW := func(r tracev2.Range) bool { + handleSTW := func(r Range) bool { return flags&UtilSTW != 0 && isGCSTW(r) } - handleMarkAssist := func(r tracev2.Range) bool { + handleMarkAssist := func(r Range) bool { return flags&UtilAssist != 0 && isGCMarkAssist(r) } - handleSweep := func(r tracev2.Range) bool { + handleSweep := func(r Range) bool { return flags&UtilSweep != 0 && isGCSweep(r) } // Iterate through the trace, tracking mutator utilization. - var lastEv *tracev2.Event + var lastEv *Event for i := range events { ev := &events[i] lastEv = ev // Process the event. switch ev.Kind() { - case tracev2.EventSync: + case EventSync: seenSync = true - case tracev2.EventMetric: + case EventMetric: m := ev.Metric() if m.Name != "/sched/gomaxprocs:threads" { break @@ -135,7 +134,7 @@ func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUt } switch ev.Kind() { - case tracev2.EventRangeActive: + case EventRangeActive: if seenSync { // If we've seen a sync, then we can be sure we're not finding out about // something late; we have complete information after that point, and these @@ -187,7 +186,7 @@ func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUt // After accounting for the portion we missed, this just acts like the // beginning of a new range. fallthrough - case tracev2.EventRangeBegin: + case EventRangeBegin: r := ev.Range() if handleSTW(r) { stw++ @@ -195,11 +194,11 @@ func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUt ps[ev.Proc()].gc++ } else if handleMarkAssist(r) { ps[ev.Proc()].gc++ - if g := r.Scope.Goroutine(); g != tracev2.NoGoroutine { + if g := r.Scope.Goroutine(); g != NoGoroutine { inGC[g] = true } } - case tracev2.EventRangeEnd: + case EventRangeEnd: r := ev.Range() if handleSTW(r) { stw-- @@ -207,13 +206,13 @@ func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUt ps[ev.Proc()].gc-- } else if handleMarkAssist(r) { ps[ev.Proc()].gc-- - if g := r.Scope.Goroutine(); g != tracev2.NoGoroutine { + if g := r.Scope.Goroutine(); g != NoGoroutine { delete(inGC, g) } } - case tracev2.EventStateTransition: + case EventStateTransition: st := ev.StateTransition() - if st.Resource.Kind != tracev2.ResourceGoroutine { + if st.Resource.Kind != ResourceGoroutine { break } old, new := st.Goroutine() @@ -228,7 +227,7 @@ func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUt } } states[g] = new - case tracev2.EventLabel: + case EventLabel: l := ev.Label() if flags&UtilBackground != 0 && strings.HasPrefix(l.Label, "GC ") && l.Label != "GC (idle)" { // Background mark worker. @@ -917,14 +916,14 @@ func (in *integrator) next(time int64) int64 { return 1<<63 - 1 } -func isGCSTW(r tracev2.Range) bool { +func isGCSTW(r Range) bool { return strings.HasPrefix(r.Name, "stop-the-world") && strings.Contains(r.Name, "GC") } -func isGCMarkAssist(r tracev2.Range) bool { +func isGCMarkAssist(r Range) bool { return r.Name == "GC mark assist" } -func isGCSweep(r tracev2.Range) bool { +func isGCSweep(r Range) bool { return r.Name == "GC incremental sweep" } diff --git a/src/internal/trace/gc_test.go b/src/internal/trace/gc_test.go index a74d59d8b7..39f28cc0f6 100644 --- a/src/internal/trace/gc_test.go +++ b/src/internal/trace/gc_test.go @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package trace +package trace_test import ( - tracev2 "internal/trace/v2" - "internal/trace/v2/testtrace" + "internal/trace" + "internal/trace/testtrace" "io" "math" "testing" @@ -32,7 +32,7 @@ func TestMMU(t *testing.T) { // 0.5 * * * * // 0.0 ***** ***** // 0 1 2 3 4 5 - util := [][]MutatorUtil{{ + util := [][]trace.MutatorUtil{{ {0e9, 1}, {1e9, 0}, {2e9, 1}, @@ -40,7 +40,7 @@ func TestMMU(t *testing.T) { {4e9, 1}, {5e9, 0}, }} - mmuCurve := NewMMUCurve(util) + mmuCurve := trace.NewMMUCurve(util) for _, test := range []struct { window time.Duration @@ -84,8 +84,8 @@ func TestMMUTrace(t *testing.T) { // test input too big for all.bash t.Skip("skipping in -short mode") } - check := func(t *testing.T, mu [][]MutatorUtil) { - mmuCurve := NewMMUCurve(mu) + check := func(t *testing.T, mu [][]trace.MutatorUtil) { + mmuCurve := trace.NewMMUCurve(mu) // Test the optimized implementation against the "obviously // correct" implementation. @@ -101,9 +101,9 @@ func TestMMUTrace(t *testing.T) { // optimization. We don't have a simple testing implementation // of MUDs (the simplest implementation is still quite // complex), but this is still a pretty good test. - defer func(old int) { bandsPerSeries = old }(bandsPerSeries) - bandsPerSeries = 1 - mmuCurve2 := NewMMUCurve(mu) + defer func(old int) { trace.BandsPerSeries = old }(trace.BandsPerSeries) + trace.BandsPerSeries = 1 + mmuCurve2 := trace.NewMMUCurve(mu) quantiles := []float64{0, 1 - .999, 1 - .99} for window := time.Microsecond; window < time.Second; window *= 10 { mud1 := mmuCurve.MUD(window, quantiles) @@ -117,13 +117,13 @@ func TestMMUTrace(t *testing.T) { } } t.Run("V2", func(t *testing.T) { - testPath := "v2/testdata/tests/go122-gc-stress.test" + testPath := "testdata/tests/go122-gc-stress.test" r, _, err := testtrace.ParseFile(testPath) if err != nil { t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } - var events []tracev2.Event - tr, err := tracev2.NewReader(r) + var events []trace.Event + tr, err := trace.NewReader(r) if err != nil { t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } @@ -138,11 +138,11 @@ func TestMMUTrace(t *testing.T) { events = append(events, ev) } // Pass the trace through MutatorUtilizationV2 and check it. - check(t, MutatorUtilizationV2(events, UtilSTW|UtilBackground|UtilAssist)) + check(t, trace.MutatorUtilizationV2(events, trace.UtilSTW|trace.UtilBackground|trace.UtilAssist)) }) } -func mmuSlow(util []MutatorUtil, window time.Duration) (mmu float64) { +func mmuSlow(util []trace.MutatorUtil, window time.Duration) (mmu float64) { if max := time.Duration(util[len(util)-1].Time - util[0].Time); window > max { window = max } @@ -151,9 +151,9 @@ func mmuSlow(util []MutatorUtil, window time.Duration) (mmu float64) { // muInWindow returns the mean mutator utilization between // util[0].Time and end. - muInWindow := func(util []MutatorUtil, end int64) float64 { + muInWindow := func(util []trace.MutatorUtil, end int64) float64 { total := 0.0 - var prevU MutatorUtil + var prevU trace.MutatorUtil for _, u := range util { if u.Time > end { total += prevU.Util * float64(end-prevU.Time) @@ -177,7 +177,7 @@ func mmuSlow(util []MutatorUtil, window time.Duration) (mmu float64) { update() // Reverse the trace. Slightly subtle because each MutatorUtil // is a *change*. - rutil := make([]MutatorUtil, len(util)) + rutil := make([]trace.MutatorUtil, len(util)) if util[len(util)-1].Util != 0 { panic("irreversible trace") } @@ -186,7 +186,7 @@ func mmuSlow(util []MutatorUtil, window time.Duration) (mmu float64) { if i != 0 { util1 = util[i-1].Util } - rutil[len(rutil)-i-1] = MutatorUtil{Time: -u.Time, Util: util1} + rutil[len(rutil)-i-1] = trace.MutatorUtil{Time: -u.Time, Util: util1} } util = rutil // Consider all right-aligned windows. diff --git a/src/internal/trace/generation.go b/src/internal/trace/generation.go new file mode 100644 index 0000000000..c67bfdba16 --- /dev/null +++ b/src/internal/trace/generation.go @@ -0,0 +1,446 @@ +// 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. + +package trace + +import ( + "bufio" + "bytes" + "cmp" + "encoding/binary" + "fmt" + "io" + "slices" + "strings" + + "internal/trace/event" + "internal/trace/event/go122" +) + +// generation contains all the trace data for a single +// trace generation. It is purely data: it does not +// track any parse state nor does it contain a cursor +// into the generation. +type generation struct { + gen uint64 + batches map[ThreadID][]batch + cpuSamples []cpuSample + *evTable +} + +// spilledBatch represents a batch that was read out for the next generation, +// while reading the previous one. It's passed on when parsing the next +// generation. +type spilledBatch struct { + gen uint64 + *batch +} + +// readGeneration buffers and decodes the structural elements of a trace generation +// out of r. spill is the first batch of the new generation (already buffered and +// parsed from reading the last generation). Returns the generation and the first +// batch read of the next generation, if any. +// +// If gen is non-nil, it is valid and must be processed before handling the returned +// error. +func readGeneration(r *bufio.Reader, spill *spilledBatch) (*generation, *spilledBatch, error) { + g := &generation{ + evTable: &evTable{ + pcs: make(map[uint64]frame), + }, + batches: make(map[ThreadID][]batch), + } + // Process the spilled batch. + if spill != nil { + g.gen = spill.gen + if err := processBatch(g, *spill.batch); err != nil { + return nil, nil, err + } + spill = nil + } + // Read batches one at a time until we either hit EOF or + // the next generation. + var spillErr error + for { + b, gen, err := readBatch(r) + if err == io.EOF { + break + } + if err != nil { + if g.gen != 0 { + // This is an error reading the first batch of the next generation. + // This is fine. Let's forge ahead assuming that what we've got so + // far is fine. + spillErr = err + break + } + return nil, nil, err + } + if gen == 0 { + // 0 is a sentinel used by the runtime, so we'll never see it. + return nil, nil, fmt.Errorf("invalid generation number %d", gen) + } + if g.gen == 0 { + // Initialize gen. + g.gen = gen + } + if gen == g.gen+1 { // TODO: advance this the same way the runtime does. + spill = &spilledBatch{gen: gen, batch: &b} + break + } + if gen != g.gen { + // N.B. Fail as fast as possible if we see this. At first it + // may seem prudent to be fault-tolerant and assume we have a + // complete generation, parsing and returning that first. However, + // if the batches are mixed across generations then it's likely + // we won't be able to parse this generation correctly at all. + // Rather than return a cryptic error in that case, indicate the + // problem as soon as we see it. + return nil, nil, fmt.Errorf("generations out of order") + } + if err := processBatch(g, b); err != nil { + return nil, nil, err + } + } + + // Check some invariants. + if g.freq == 0 { + return nil, nil, fmt.Errorf("no frequency event found") + } + // N.B. Trust that the batch order is correct. We can't validate the batch order + // by timestamp because the timestamps could just be plain wrong. The source of + // truth is the order things appear in the trace and the partial order sequence + // numbers on certain events. If it turns out the batch order is actually incorrect + // we'll very likely fail to advance a partial order from the frontier. + + // Compactify stacks and strings for better lookup performance later. + g.stacks.compactify() + g.strings.compactify() + + // Validate stacks. + if err := validateStackStrings(&g.stacks, &g.strings, g.pcs); err != nil { + return nil, nil, err + } + + // Fix up the CPU sample timestamps, now that we have freq. + for i := range g.cpuSamples { + s := &g.cpuSamples[i] + s.time = g.freq.mul(timestamp(s.time)) + } + // Sort the CPU samples. + slices.SortFunc(g.cpuSamples, func(a, b cpuSample) int { + return cmp.Compare(a.time, b.time) + }) + return g, spill, spillErr +} + +// processBatch adds the batch to the generation. +func processBatch(g *generation, b batch) error { + switch { + case b.isStringsBatch(): + if err := addStrings(&g.strings, b); err != nil { + return err + } + case b.isStacksBatch(): + if err := addStacks(&g.stacks, g.pcs, b); err != nil { + return err + } + case b.isCPUSamplesBatch(): + samples, err := addCPUSamples(g.cpuSamples, b) + if err != nil { + return err + } + g.cpuSamples = samples + case b.isFreqBatch(): + freq, err := parseFreq(b) + if err != nil { + return err + } + if g.freq != 0 { + return fmt.Errorf("found multiple frequency events") + } + g.freq = freq + case b.exp != event.NoExperiment: + if err := addExperimentalData(g.expData, b); err != nil { + return err + } + default: + g.batches[b.m] = append(g.batches[b.m], b) + } + return nil +} + +// validateStackStrings makes sure all the string references in +// the stack table are present in the string table. +func validateStackStrings( + stacks *dataTable[stackID, stack], + strings *dataTable[stringID, string], + frames map[uint64]frame, +) error { + var err error + stacks.forEach(func(id stackID, stk stack) bool { + for _, pc := range stk.pcs { + frame, ok := frames[pc] + if !ok { + err = fmt.Errorf("found unknown pc %x for stack %d", pc, id) + return false + } + _, ok = strings.get(frame.funcID) + if !ok { + err = fmt.Errorf("found invalid func string ID %d for stack %d", frame.funcID, id) + return false + } + _, ok = strings.get(frame.fileID) + if !ok { + err = fmt.Errorf("found invalid file string ID %d for stack %d", frame.fileID, id) + return false + } + } + return true + }) + return err +} + +// addStrings takes a batch whose first byte is an EvStrings event +// (indicating that the batch contains only strings) and adds each +// string contained therein to the provided strings map. +func addStrings(stringTable *dataTable[stringID, string], b batch) error { + if !b.isStringsBatch() { + return fmt.Errorf("internal error: addStrings called on non-string batch") + } + r := bytes.NewReader(b.data) + hdr, err := r.ReadByte() // Consume the EvStrings byte. + if err != nil || event.Type(hdr) != go122.EvStrings { + return fmt.Errorf("missing strings batch header") + } + + var sb strings.Builder + for r.Len() != 0 { + // Read the header. + ev, err := r.ReadByte() + if err != nil { + return err + } + if event.Type(ev) != go122.EvString { + return fmt.Errorf("expected string event, got %d", ev) + } + + // Read the string's ID. + id, err := binary.ReadUvarint(r) + if err != nil { + return err + } + + // Read the string's length. + len, err := binary.ReadUvarint(r) + if err != nil { + return err + } + if len > go122.MaxStringSize { + return fmt.Errorf("invalid string size %d, maximum is %d", len, go122.MaxStringSize) + } + + // Copy out the string. + n, err := io.CopyN(&sb, r, int64(len)) + if n != int64(len) { + return fmt.Errorf("failed to read full string: read %d but wanted %d", n, len) + } + if err != nil { + return fmt.Errorf("copying string data: %w", err) + } + + // Add the string to the map. + s := sb.String() + sb.Reset() + if err := stringTable.insert(stringID(id), s); err != nil { + return err + } + } + return nil +} + +// addStacks takes a batch whose first byte is an EvStacks event +// (indicating that the batch contains only stacks) and adds each +// string contained therein to the provided stacks map. +func addStacks(stackTable *dataTable[stackID, stack], pcs map[uint64]frame, b batch) error { + if !b.isStacksBatch() { + return fmt.Errorf("internal error: addStacks called on non-stacks batch") + } + r := bytes.NewReader(b.data) + hdr, err := r.ReadByte() // Consume the EvStacks byte. + if err != nil || event.Type(hdr) != go122.EvStacks { + return fmt.Errorf("missing stacks batch header") + } + + for r.Len() != 0 { + // Read the header. + ev, err := r.ReadByte() + if err != nil { + return err + } + if event.Type(ev) != go122.EvStack { + return fmt.Errorf("expected stack event, got %d", ev) + } + + // Read the stack's ID. + id, err := binary.ReadUvarint(r) + if err != nil { + return err + } + + // Read how many frames are in each stack. + nFrames, err := binary.ReadUvarint(r) + if err != nil { + return err + } + if nFrames > go122.MaxFramesPerStack { + return fmt.Errorf("invalid stack size %d, maximum is %d", nFrames, go122.MaxFramesPerStack) + } + + // Each frame consists of 4 fields: pc, funcID (string), fileID (string), line. + frames := make([]uint64, 0, nFrames) + for i := uint64(0); i < nFrames; i++ { + // Read the frame data. + pc, err := binary.ReadUvarint(r) + if err != nil { + return fmt.Errorf("reading frame %d's PC for stack %d: %w", i+1, id, err) + } + funcID, err := binary.ReadUvarint(r) + if err != nil { + return fmt.Errorf("reading frame %d's funcID for stack %d: %w", i+1, id, err) + } + fileID, err := binary.ReadUvarint(r) + if err != nil { + return fmt.Errorf("reading frame %d's fileID for stack %d: %w", i+1, id, err) + } + line, err := binary.ReadUvarint(r) + if err != nil { + return fmt.Errorf("reading frame %d's line for stack %d: %w", i+1, id, err) + } + frames = append(frames, pc) + + if _, ok := pcs[pc]; !ok { + pcs[pc] = frame{ + pc: pc, + funcID: stringID(funcID), + fileID: stringID(fileID), + line: line, + } + } + } + + // Add the stack to the map. + if err := stackTable.insert(stackID(id), stack{pcs: frames}); err != nil { + return err + } + } + return nil +} + +// addCPUSamples takes a batch whose first byte is an EvCPUSamples event +// (indicating that the batch contains only CPU samples) and adds each +// sample contained therein to the provided samples list. +func addCPUSamples(samples []cpuSample, b batch) ([]cpuSample, error) { + if !b.isCPUSamplesBatch() { + return nil, fmt.Errorf("internal error: addCPUSamples called on non-CPU-sample batch") + } + r := bytes.NewReader(b.data) + hdr, err := r.ReadByte() // Consume the EvCPUSamples byte. + if err != nil || event.Type(hdr) != go122.EvCPUSamples { + return nil, fmt.Errorf("missing CPU samples batch header") + } + + for r.Len() != 0 { + // Read the header. + ev, err := r.ReadByte() + if err != nil { + return nil, err + } + if event.Type(ev) != go122.EvCPUSample { + return nil, fmt.Errorf("expected CPU sample event, got %d", ev) + } + + // Read the sample's timestamp. + ts, err := binary.ReadUvarint(r) + if err != nil { + return nil, err + } + + // Read the sample's M. + m, err := binary.ReadUvarint(r) + if err != nil { + return nil, err + } + mid := ThreadID(m) + + // Read the sample's P. + p, err := binary.ReadUvarint(r) + if err != nil { + return nil, err + } + pid := ProcID(p) + + // Read the sample's G. + g, err := binary.ReadUvarint(r) + if err != nil { + return nil, err + } + goid := GoID(g) + if g == 0 { + goid = NoGoroutine + } + + // Read the sample's stack. + s, err := binary.ReadUvarint(r) + if err != nil { + return nil, err + } + + // Add the sample to the slice. + samples = append(samples, cpuSample{ + schedCtx: schedCtx{ + M: mid, + P: pid, + G: goid, + }, + time: Time(ts), // N.B. this is really a "timestamp," not a Time. + stack: stackID(s), + }) + } + return samples, nil +} + +// parseFreq parses out a lone EvFrequency from a batch. +func parseFreq(b batch) (frequency, error) { + if !b.isFreqBatch() { + return 0, fmt.Errorf("internal error: parseFreq called on non-frequency batch") + } + r := bytes.NewReader(b.data) + r.ReadByte() // Consume the EvFrequency byte. + + // Read the frequency. It'll come out as timestamp units per second. + f, err := binary.ReadUvarint(r) + if err != nil { + return 0, err + } + // Convert to nanoseconds per timestamp unit. + return frequency(1.0 / (float64(f) / 1e9)), nil +} + +// addExperimentalData takes an experimental batch and adds it to the ExperimentalData +// for the experiment its a part of. +func addExperimentalData(expData map[event.Experiment]*ExperimentalData, b batch) error { + if b.exp == event.NoExperiment { + return fmt.Errorf("internal error: addExperimentalData called on non-experimental batch") + } + ed, ok := expData[b.exp] + if !ok { + ed = new(ExperimentalData) + } + ed.Batches = append(ed.Batches, ExperimentalBatch{ + Thread: b.m, + Data: b.data, + }) + return nil +} diff --git a/src/internal/trace/internal/oldtrace/order.go b/src/internal/trace/internal/oldtrace/order.go new file mode 100644 index 0000000000..4cd3def211 --- /dev/null +++ b/src/internal/trace/internal/oldtrace/order.go @@ -0,0 +1,186 @@ +// Copyright 2024 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 oldtrace + +import "errors" + +type orderEvent struct { + ev Event + proc *proc +} + +type gStatus int + +type gState struct { + seq uint64 + status gStatus +} + +const ( + gDead gStatus = iota + gRunnable + gRunning + gWaiting + + unordered = ^uint64(0) + garbage = ^uint64(0) - 1 + noseq = ^uint64(0) + seqinc = ^uint64(0) - 1 +) + +// stateTransition returns goroutine state (sequence and status) when the event +// becomes ready for merging (init) and the goroutine state after the event (next). +func stateTransition(ev *Event) (g uint64, init, next gState) { + // Note that we have an explicit return in each case, as that produces slightly better code (tested on Go 1.19). + + switch ev.Type { + case EvGoCreate: + g = ev.Args[0] + init = gState{0, gDead} + next = gState{1, gRunnable} + return + case EvGoWaiting, EvGoInSyscall: + g = ev.G + init = gState{1, gRunnable} + next = gState{2, gWaiting} + return + case EvGoStart, EvGoStartLabel: + g = ev.G + init = gState{ev.Args[1], gRunnable} + next = gState{ev.Args[1] + 1, gRunning} + return + case EvGoStartLocal: + // noseq means that this event is ready for merging as soon as + // frontier reaches it (EvGoStartLocal is emitted on the same P + // as the corresponding EvGoCreate/EvGoUnblock, and thus the latter + // is already merged). + // seqinc is a stub for cases when event increments g sequence, + // but since we don't know current seq we also don't know next seq. + g = ev.G + init = gState{noseq, gRunnable} + next = gState{seqinc, gRunning} + return + case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, + EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep, + EvGoSysBlock, EvGoBlockGC: + g = ev.G + init = gState{noseq, gRunning} + next = gState{noseq, gWaiting} + return + case EvGoSched, EvGoPreempt: + g = ev.G + init = gState{noseq, gRunning} + next = gState{noseq, gRunnable} + return + case EvGoUnblock, EvGoSysExit: + g = ev.Args[0] + init = gState{ev.Args[1], gWaiting} + next = gState{ev.Args[1] + 1, gRunnable} + return + case EvGoUnblockLocal, EvGoSysExitLocal: + g = ev.Args[0] + init = gState{noseq, gWaiting} + next = gState{seqinc, gRunnable} + return + case EvGCStart: + g = garbage + init = gState{ev.Args[0], gDead} + next = gState{ev.Args[0] + 1, gDead} + return + default: + // no ordering requirements + g = unordered + return + } +} + +func transitionReady(g uint64, curr, init gState) bool { + return g == unordered || (init.seq == noseq || init.seq == curr.seq) && init.status == curr.status +} + +func transition(gs map[uint64]gState, g uint64, init, next gState) error { + if g == unordered { + return nil + } + curr := gs[g] + if !transitionReady(g, curr, init) { + // See comment near the call to transition, where we're building the frontier, for details on how this could + // possibly happen. + return errors.New("encountered impossible goroutine state transition") + } + switch next.seq { + case noseq: + next.seq = curr.seq + case seqinc: + next.seq = curr.seq + 1 + } + gs[g] = next + return nil +} + +type orderEventList []orderEvent + +func (l *orderEventList) Less(i, j int) bool { + return (*l)[i].ev.Ts < (*l)[j].ev.Ts +} + +type eventList []Event + +func (l *eventList) Len() int { + return len(*l) +} + +func (l *eventList) Less(i, j int) bool { + return (*l)[i].Ts < (*l)[j].Ts +} + +func (l *eventList) Swap(i, j int) { + (*l)[i], (*l)[j] = (*l)[j], (*l)[i] +} + +func (h *orderEventList) Push(x orderEvent) { + *h = append(*h, x) + heapUp(h, len(*h)-1) +} + +func (h *orderEventList) Pop() orderEvent { + n := len(*h) - 1 + (*h)[0], (*h)[n] = (*h)[n], (*h)[0] + heapDown(h, 0, n) + x := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return x +} + +func heapUp(h *orderEventList, j int) { + for { + i := (j - 1) / 2 // parent + if i == j || !h.Less(j, i) { + break + } + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] + j = i + } +} + +func heapDown(h *orderEventList, i0, n int) bool { + i := i0 + for { + j1 := 2*i + 1 + if j1 >= n || j1 < 0 { // j1 < 0 after int overflow + break + } + j := j1 // left child + if j2 := j1 + 1; j2 < n && h.Less(j2, j1) { + j = j2 // = 2*i + 2 // right child + } + if !h.Less(j, i) { + break + } + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] + i = j + } + return i > i0 +} diff --git a/src/internal/trace/internal/oldtrace/parser.go b/src/internal/trace/internal/oldtrace/parser.go new file mode 100644 index 0000000000..3775c78f1a --- /dev/null +++ b/src/internal/trace/internal/oldtrace/parser.go @@ -0,0 +1,1540 @@ +// Copyright 2024 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 oldtrace implements a parser for Go execution traces from versions +// 1.11–1.21. +// +// The package started as a copy of Go 1.19's internal/trace, but has been +// optimized to be faster while using less memory and fewer allocations. It has +// been further modified for the specific purpose of converting traces to the +// new 1.22+ format. +package oldtrace + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "internal/trace/event" + "internal/trace/version" + "io" + "math" + "sort" +) + +// Timestamp represents a count of nanoseconds since the beginning of the trace. +// They can only be meaningfully compared with other timestamps from the same +// trace. +type Timestamp int64 + +// Event describes one event in the trace. +type Event struct { + // The Event type is carefully laid out to optimize its size and to avoid + // pointers, the latter so that the garbage collector won't have to scan any + // memory of our millions of events. + + Ts Timestamp // timestamp in nanoseconds + G uint64 // G on which the event happened + Args [4]uint64 // event-type-specific arguments + StkID uint32 // unique stack ID + P int32 // P on which the event happened (can be a real P or one of TimerP, NetpollP, SyscallP) + Type event.Type // one of Ev* +} + +// Frame is a frame in stack traces. +type Frame struct { + PC uint64 + // string ID of the function name + Fn uint64 + // string ID of the file name + File uint64 + Line int +} + +const ( + // Special P identifiers: + FakeP = 1000000 + iota + TimerP // contains timer unblocks + NetpollP // contains network unblocks + SyscallP // contains returns from syscalls + GCP // contains GC state + ProfileP // contains recording of CPU profile samples +) + +// Trace is the result of Parse. +type Trace struct { + Version version.Version + + // Events is the sorted list of Events in the trace. + Events Events + // Stacks is the stack traces (stored as slices of PCs), keyed by stack IDs + // from the trace. + Stacks map[uint32][]uint64 + PCs map[uint64]Frame + Strings map[uint64]string + InlineStrings []string +} + +// batchOffset records the byte offset of, and number of events in, a batch. A +// batch is a sequence of events emitted by a P. Events within a single batch +// are sorted by time. +type batchOffset struct { + offset int + numEvents int +} + +type parser struct { + ver version.Version + data []byte + off int + + strings map[uint64]string + inlineStrings []string + inlineStringsMapping map[string]int + // map from Ps to their batch offsets + batchOffsets map[int32][]batchOffset + stacks map[uint32][]uint64 + stacksData []uint64 + ticksPerSec int64 + pcs map[uint64]Frame + cpuSamples []Event + timerGoids map[uint64]bool + + // state for readRawEvent + args []uint64 + + // state for parseEvent + lastTs Timestamp + lastG uint64 + // map from Ps to the last Gs that ran on them + lastGs map[int32]uint64 + lastP int32 +} + +func (p *parser) discard(n uint64) bool { + if n > math.MaxInt { + return false + } + if noff := p.off + int(n); noff < p.off || noff > len(p.data) { + return false + } else { + p.off = noff + } + return true +} + +func newParser(r io.Reader, ver version.Version) (*parser, error) { + var buf []byte + if seeker, ok := r.(io.Seeker); ok { + // Determine the size of the reader so that we can allocate a buffer + // without having to grow it later. + cur, err := seeker.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + end, err := seeker.Seek(0, io.SeekEnd) + if err != nil { + return nil, err + } + _, err = seeker.Seek(cur, io.SeekStart) + if err != nil { + return nil, err + } + + buf = make([]byte, end-cur) + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, err + } + } else { + var err error + buf, err = io.ReadAll(r) + if err != nil { + return nil, err + } + } + return &parser{data: buf, ver: ver, timerGoids: make(map[uint64]bool)}, nil +} + +// Parse parses Go execution traces from versions 1.11–1.21. The provided reader +// will be read to completion and the entire trace will be materialized in +// memory. That is, this function does not allow incremental parsing. +// +// The reader has to be positioned just after the trace header and vers needs to +// be the version of the trace. This can be achieved by using +// version.ReadHeader. +func Parse(r io.Reader, vers version.Version) (Trace, error) { + // We accept the version as an argument because internal/trace/v2 will have + // already read the version to determine which parser to use. + p, err := newParser(r, vers) + if err != nil { + return Trace{}, err + } + return p.parse() +} + +// parse parses, post-processes and verifies the trace. +func (p *parser) parse() (Trace, error) { + defer func() { + p.data = nil + }() + + // We parse a trace by running the following steps in order: + // + // 1. In the initial pass we collect information about batches (their + // locations and sizes.) We also parse CPU profiling samples in this + // step, simply to reduce the number of full passes that we need. + // + // 2. In the second pass we parse batches and merge them into a globally + // ordered event stream. This uses the batch information from the first + // pass to quickly find batches. + // + // 3. After all events have been parsed we convert their timestamps from CPU + // ticks to wall time. Furthermore we move timers and syscalls to + // dedicated, fake Ps. + // + // 4. Finally, we validate the trace. + + p.strings = make(map[uint64]string) + p.batchOffsets = make(map[int32][]batchOffset) + p.lastGs = make(map[int32]uint64) + p.stacks = make(map[uint32][]uint64) + p.pcs = make(map[uint64]Frame) + p.inlineStringsMapping = make(map[string]int) + + if err := p.collectBatchesAndCPUSamples(); err != nil { + return Trace{}, err + } + + events, err := p.parseEventBatches() + if err != nil { + return Trace{}, err + } + + if p.ticksPerSec == 0 { + return Trace{}, errors.New("no EvFrequency event") + } + + if events.Len() > 0 { + // Translate cpu ticks to real time. + minTs := events.Ptr(0).Ts + // Use floating point to avoid integer overflows. + freq := 1e9 / float64(p.ticksPerSec) + for i := 0; i < events.Len(); i++ { + ev := events.Ptr(i) + ev.Ts = Timestamp(float64(ev.Ts-minTs) * freq) + // Move timers and syscalls to separate fake Ps. + if p.timerGoids[ev.G] && ev.Type == EvGoUnblock { + ev.P = TimerP + } + if ev.Type == EvGoSysExit { + ev.P = SyscallP + } + } + } + + if err := p.postProcessTrace(events); err != nil { + return Trace{}, err + } + + res := Trace{ + Version: p.ver, + Events: events, + Stacks: p.stacks, + Strings: p.strings, + InlineStrings: p.inlineStrings, + PCs: p.pcs, + } + return res, nil +} + +// rawEvent is a helper type used during parsing. +type rawEvent struct { + typ event.Type + args []uint64 + sargs []string + + // if typ == EvBatch, these fields describe the batch. + batchPid int32 + batchOffset int +} + +type proc struct { + pid int32 + // the remaining events in the current batch + events []Event + // buffer for reading batches into, aliased by proc.events + buf []Event + + // there are no more batches left + done bool +} + +const eventsBucketSize = 524288 // 32 MiB of events + +type Events struct { + // Events is a slice of slices that grows one slice of size eventsBucketSize + // at a time. This avoids the O(n) cost of slice growth in append, and + // additionally allows consumers to drop references to parts of the data, + // freeing memory piecewise. + n int + buckets []*[eventsBucketSize]Event + off int +} + +// grow grows the slice by one and returns a pointer to the new element, without +// overwriting it. +func (l *Events) grow() *Event { + a, b := l.index(l.n) + if a >= len(l.buckets) { + l.buckets = append(l.buckets, new([eventsBucketSize]Event)) + } + ptr := &l.buckets[a][b] + l.n++ + return ptr +} + +// append appends v to the slice and returns a pointer to the new element. +func (l *Events) append(v Event) *Event { + ptr := l.grow() + *ptr = v + return ptr +} + +func (l *Events) Ptr(i int) *Event { + a, b := l.index(i + l.off) + return &l.buckets[a][b] +} + +func (l *Events) index(i int) (int, int) { + // Doing the division on uint instead of int compiles this function to a + // shift and an AND (for power of 2 bucket sizes), versus a whole bunch of + // instructions for int. + return int(uint(i) / eventsBucketSize), int(uint(i) % eventsBucketSize) +} + +func (l *Events) Len() int { + return l.n - l.off +} + +func (l *Events) Less(i, j int) bool { + return l.Ptr(i).Ts < l.Ptr(j).Ts +} + +func (l *Events) Swap(i, j int) { + *l.Ptr(i), *l.Ptr(j) = *l.Ptr(j), *l.Ptr(i) +} + +func (l *Events) Pop() (*Event, bool) { + if l.off == l.n { + return nil, false + } + a, b := l.index(l.off) + ptr := &l.buckets[a][b] + l.off++ + if b == eventsBucketSize-1 || l.off == l.n { + // We've consumed the last event from the bucket, so drop the bucket and + // allow GC to collect it. + l.buckets[a] = nil + } + return ptr, true +} + +func (l *Events) All() func(yield func(ev *Event) bool) { + return func(yield func(ev *Event) bool) { + for i := 0; i < l.Len(); i++ { + a, b := l.index(i + l.off) + ptr := &l.buckets[a][b] + if !yield(ptr) { + return + } + } + } +} + +// parseEventBatches reads per-P event batches and merges them into a single, consistent +// stream. The high level idea is as follows. Events within an individual batch +// are in correct order, because they are emitted by a single P. So we need to +// produce a correct interleaving of the batches. To do this we take first +// unmerged event from each batch (frontier). Then choose subset that is "ready" +// to be merged, that is, events for which all dependencies are already merged. +// Then we choose event with the lowest timestamp from the subset, merge it and +// repeat. This approach ensures that we form a consistent stream even if +// timestamps are incorrect (condition observed on some machines). +func (p *parser) parseEventBatches() (Events, error) { + // The ordering of CPU profile sample events in the data stream is based on + // when each run of the signal handler was able to acquire the spinlock, + // with original timestamps corresponding to when ReadTrace pulled the data + // off of the profBuf queue. Re-sort them by the timestamp we captured + // inside the signal handler. + sort.Sort((*eventList)(&p.cpuSamples)) + + allProcs := make([]proc, 0, len(p.batchOffsets)) + for pid := range p.batchOffsets { + allProcs = append(allProcs, proc{pid: pid}) + } + allProcs = append(allProcs, proc{pid: ProfileP, events: p.cpuSamples}) + + events := Events{} + + // Merge events as long as at least one P has more events + gs := make(map[uint64]gState) + // Note: technically we don't need a priority queue here. We're only ever + // interested in the earliest elligible event, which means we just have to + // track the smallest element. However, in practice, the priority queue + // performs better, because for each event we only have to compute its state + // transition once, not on each iteration. If it was elligible before, it'll + // already be in the queue. Furthermore, on average, we only have one P to + // look at in each iteration, because all other Ps are already in the queue. + var frontier orderEventList + + availableProcs := make([]*proc, len(allProcs)) + for i := range allProcs { + availableProcs[i] = &allProcs[i] + } + for { + pidLoop: + for i := 0; i < len(availableProcs); i++ { + proc := availableProcs[i] + + for len(proc.events) == 0 { + // Call loadBatch in a loop because sometimes batches are empty + evs, err := p.loadBatch(proc.pid, proc.buf[:0]) + proc.buf = evs[:0] + if err == io.EOF { + // This P has no more events + proc.done = true + availableProcs[i], availableProcs[len(availableProcs)-1] = availableProcs[len(availableProcs)-1], availableProcs[i] + availableProcs = availableProcs[:len(availableProcs)-1] + // We swapped the element at i with another proc, so look at + // the index again + i-- + continue pidLoop + } else if err != nil { + return Events{}, err + } else { + proc.events = evs + } + } + + ev := &proc.events[0] + g, init, _ := stateTransition(ev) + + // TODO(dh): This implementation matches the behavior of the + // upstream 'go tool trace', and works in practice, but has run into + // the following inconsistency during fuzzing: what happens if + // multiple Ps have events for the same G? While building the + // frontier we will check all of the events against the current + // state of the G. However, when we process the frontier, the state + // of the G changes, and a transition that was valid while building + // the frontier may no longer be valid when processing the frontier. + // Is this something that can happen for real, valid traces, or is + // this only possible with corrupt data? + if !transitionReady(g, gs[g], init) { + continue + } + proc.events = proc.events[1:] + availableProcs[i], availableProcs[len(availableProcs)-1] = availableProcs[len(availableProcs)-1], availableProcs[i] + availableProcs = availableProcs[:len(availableProcs)-1] + frontier.Push(orderEvent{*ev, proc}) + + // We swapped the element at i with another proc, so look at the + // index again + i-- + } + + if len(frontier) == 0 { + for i := range allProcs { + if !allProcs[i].done { + return Events{}, fmt.Errorf("no consistent ordering of events possible") + } + } + break + } + f := frontier.Pop() + + // We're computing the state transition twice, once when computing the + // frontier, and now to apply the transition. This is fine because + // stateTransition is a pure function. Computing it again is cheaper + // than storing large items in the frontier. + g, init, next := stateTransition(&f.ev) + + // Get rid of "Local" events, they are intended merely for ordering. + switch f.ev.Type { + case EvGoStartLocal: + f.ev.Type = EvGoStart + case EvGoUnblockLocal: + f.ev.Type = EvGoUnblock + case EvGoSysExitLocal: + f.ev.Type = EvGoSysExit + } + events.append(f.ev) + + if err := transition(gs, g, init, next); err != nil { + return Events{}, err + } + availableProcs = append(availableProcs, f.proc) + } + + // At this point we have a consistent stream of events. Make sure time + // stamps respect the ordering. The tests will skip (not fail) the test case + // if they see this error. + if !sort.IsSorted(&events) { + return Events{}, ErrTimeOrder + } + + // The last part is giving correct timestamps to EvGoSysExit events. The + // problem with EvGoSysExit is that actual syscall exit timestamp + // (ev.Args[2]) is potentially acquired long before event emission. So far + // we've used timestamp of event emission (ev.Ts). We could not set ev.Ts = + // ev.Args[2] earlier, because it would produce seemingly broken timestamps + // (misplaced event). We also can't simply update the timestamp and resort + // events, because if timestamps are broken we will misplace the event and + // later report logically broken trace (instead of reporting broken + // timestamps). + lastSysBlock := make(map[uint64]Timestamp) + for i := 0; i < events.Len(); i++ { + ev := events.Ptr(i) + switch ev.Type { + case EvGoSysBlock, EvGoInSyscall: + lastSysBlock[ev.G] = ev.Ts + case EvGoSysExit: + ts := Timestamp(ev.Args[2]) + if ts == 0 { + continue + } + block := lastSysBlock[ev.G] + if block == 0 { + return Events{}, fmt.Errorf("stray syscall exit") + } + if ts < block { + return Events{}, ErrTimeOrder + } + ev.Ts = ts + } + } + sort.Stable(&events) + + return events, nil +} + +// collectBatchesAndCPUSamples records the offsets of batches and parses CPU samples. +func (p *parser) collectBatchesAndCPUSamples() error { + // Read events. + var raw rawEvent + var curP int32 + for n := uint64(0); ; n++ { + err := p.readRawEvent(skipArgs|skipStrings, &raw) + if err == io.EOF { + break + } + if err != nil { + return err + } + if raw.typ == EvNone { + continue + } + + if raw.typ == EvBatch { + bo := batchOffset{offset: raw.batchOffset} + p.batchOffsets[raw.batchPid] = append(p.batchOffsets[raw.batchPid], bo) + curP = raw.batchPid + } + + batches := p.batchOffsets[curP] + if len(batches) == 0 { + return fmt.Errorf("read event %d with current P of %d, but P has no batches yet", + raw.typ, curP) + } + batches[len(batches)-1].numEvents++ + + if raw.typ == EvCPUSample { + e := Event{Type: raw.typ} + + argOffset := 1 + narg := raw.argNum() + if len(raw.args) != narg { + return fmt.Errorf("CPU sample has wrong number of arguments: want %d, got %d", narg, len(raw.args)) + } + for i := argOffset; i < narg; i++ { + if i == narg-1 { + e.StkID = uint32(raw.args[i]) + } else { + e.Args[i-argOffset] = raw.args[i] + } + } + + e.Ts = Timestamp(e.Args[0]) + e.P = int32(e.Args[1]) + e.G = e.Args[2] + e.Args[0] = 0 + + // Most events are written out by the active P at the exact moment + // they describe. CPU profile samples are different because they're + // written to the tracing log after some delay, by a separate worker + // goroutine, into a separate buffer. + // + // We keep these in their own batch until all of the batches are + // merged in timestamp order. We also (right before the merge) + // re-sort these events by the timestamp captured in the profiling + // signal handler. + // + // Note that we're not concerned about the memory usage of storing + // all CPU samples during the indexing phase. There are orders of + // magnitude fewer CPU samples than runtime events. + p.cpuSamples = append(p.cpuSamples, e) + } + } + + return nil +} + +const ( + skipArgs = 1 << iota + skipStrings +) + +func (p *parser) readByte() (byte, bool) { + if p.off < len(p.data) && p.off >= 0 { + b := p.data[p.off] + p.off++ + return b, true + } else { + return 0, false + } +} + +func (p *parser) readFull(n int) ([]byte, error) { + if p.off >= len(p.data) || p.off < 0 || p.off+n > len(p.data) { + // p.off < 0 is impossible but makes BCE happy. + // + // We do fail outright if there's not enough data, we don't care about + // partial results. + return nil, io.ErrUnexpectedEOF + } + buf := p.data[p.off : p.off+n] + p.off += n + return buf, nil +} + +// readRawEvent reads a raw event into ev. The slices in ev are only valid until +// the next call to readRawEvent, even when storing to a different location. +func (p *parser) readRawEvent(flags uint, ev *rawEvent) error { + // The number of arguments is encoded using two bits and can thus only + // represent the values 0–3. The value 3 (on the wire) indicates that + // arguments are prefixed by their byte length, to encode >=3 arguments. + const inlineArgs = 3 + + // Read event type and number of arguments (1 byte). + b, ok := p.readByte() + if !ok { + return io.EOF + } + typ := event.Type(b << 2 >> 2) + // Most events have a timestamp before the actual arguments, so we add 1 and + // parse it like it's the first argument. EvString has a special format and + // the number of arguments doesn't matter. EvBatch writes '1' as the number + // of arguments, but actually has two: a pid and a timestamp, but here the + // timestamp is the second argument, not the first; adding 1 happens to come + // up with the correct number, but it doesn't matter, because EvBatch has + // custom logic for parsing. + // + // Note that because we're adding 1, inlineArgs == 3 describes the largest + // number of logical arguments that isn't length-prefixed, even though the + // value 3 on the wire indicates length-prefixing. For us, that becomes narg + // == 4. + narg := b>>6 + 1 + if typ == EvNone || typ >= EvCount || EventDescriptions[typ].minVersion > p.ver { + return fmt.Errorf("unknown event type %d", typ) + } + + switch typ { + case EvString: + if flags&skipStrings != 0 { + // String dictionary entry [ID, length, string]. + if _, err := p.readVal(); err != nil { + return errMalformedVarint + } + ln, err := p.readVal() + if err != nil { + return err + } + if !p.discard(ln) { + return fmt.Errorf("failed to read trace: %w", io.EOF) + } + } else { + // String dictionary entry [ID, length, string]. + id, err := p.readVal() + if err != nil { + return err + } + if id == 0 { + return errors.New("string has invalid id 0") + } + if p.strings[id] != "" { + return fmt.Errorf("string has duplicate id %d", id) + } + var ln uint64 + ln, err = p.readVal() + if err != nil { + return err + } + if ln == 0 { + return errors.New("string has invalid length 0") + } + if ln > 1e6 { + return fmt.Errorf("string has too large length %d", ln) + } + buf, err := p.readFull(int(ln)) + if err != nil { + return fmt.Errorf("failed to read trace: %w", err) + } + p.strings[id] = string(buf) + } + + ev.typ = EvNone + return nil + case EvBatch: + if want := byte(2); narg != want { + return fmt.Errorf("EvBatch has wrong number of arguments: got %d, want %d", narg, want) + } + + // -1 because we've already read the first byte of the batch + off := p.off - 1 + + pid, err := p.readVal() + if err != nil { + return err + } + if pid != math.MaxUint64 && pid > math.MaxInt32 { + return fmt.Errorf("processor ID %d is larger than maximum of %d", pid, uint64(math.MaxUint)) + } + + var pid32 int32 + if pid == math.MaxUint64 { + pid32 = -1 + } else { + pid32 = int32(pid) + } + + v, err := p.readVal() + if err != nil { + return err + } + + *ev = rawEvent{ + typ: EvBatch, + args: p.args[:0], + batchPid: pid32, + batchOffset: off, + } + ev.args = append(ev.args, pid, v) + return nil + default: + *ev = rawEvent{typ: typ, args: p.args[:0]} + if narg <= inlineArgs { + if flags&skipArgs == 0 { + for i := 0; i < int(narg); i++ { + v, err := p.readVal() + if err != nil { + return fmt.Errorf("failed to read event %d argument: %w", typ, err) + } + ev.args = append(ev.args, v) + } + } else { + for i := 0; i < int(narg); i++ { + if _, err := p.readVal(); err != nil { + return fmt.Errorf("failed to read event %d argument: %w", typ, errMalformedVarint) + } + } + } + } else { + // More than inlineArgs args, the first value is length of the event + // in bytes. + v, err := p.readVal() + if err != nil { + return fmt.Errorf("failed to read event %d argument: %w", typ, err) + } + + if limit := uint64(2048); v > limit { + // At the time of Go 1.19, v seems to be at most 128. Set 2048 + // as a generous upper limit and guard against malformed traces. + return fmt.Errorf("failed to read event %d argument: length-prefixed argument too big: %d bytes, limit is %d", typ, v, limit) + } + + if flags&skipArgs == 0 || typ == EvCPUSample { + buf, err := p.readFull(int(v)) + if err != nil { + return fmt.Errorf("failed to read trace: %w", err) + } + for len(buf) > 0 { + var v uint64 + v, buf, err = readValFrom(buf) + if err != nil { + return err + } + ev.args = append(ev.args, v) + } + } else { + // Skip over arguments + if !p.discard(v) { + return fmt.Errorf("failed to read trace: %w", io.EOF) + } + } + if typ == EvUserLog { + // EvUserLog records are followed by a value string + if flags&skipArgs == 0 { + // Read string + s, err := p.readStr() + if err != nil { + return err + } + ev.sargs = append(ev.sargs, s) + } else { + // Skip string + v, err := p.readVal() + if err != nil { + return err + } + if !p.discard(v) { + return io.EOF + } + } + } + } + + p.args = ev.args[:0] + return nil + } +} + +// loadBatch loads the next batch for pid and appends its contents to to events. +func (p *parser) loadBatch(pid int32, events []Event) ([]Event, error) { + offsets := p.batchOffsets[pid] + if len(offsets) == 0 { + return nil, io.EOF + } + n := offsets[0].numEvents + offset := offsets[0].offset + offsets = offsets[1:] + p.batchOffsets[pid] = offsets + + p.off = offset + + if cap(events) < n { + events = make([]Event, 0, n) + } + + gotHeader := false + var raw rawEvent + var ev Event + for { + err := p.readRawEvent(0, &raw) + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + if raw.typ == EvNone || raw.typ == EvCPUSample { + continue + } + if raw.typ == EvBatch { + if gotHeader { + break + } else { + gotHeader = true + } + } + + err = p.parseEvent(&raw, &ev) + if err != nil { + return nil, err + } + if ev.Type != EvNone { + events = append(events, ev) + } + } + + return events, nil +} + +func (p *parser) readStr() (s string, err error) { + sz, err := p.readVal() + if err != nil { + return "", err + } + if sz == 0 { + return "", nil + } + if sz > 1e6 { + return "", fmt.Errorf("string is too large (len=%d)", sz) + } + buf, err := p.readFull(int(sz)) + if err != nil { + return "", fmt.Errorf("failed to read trace: %w", err) + } + return string(buf), nil +} + +// parseEvent transforms raw events into events. +// It does analyze and verify per-event-type arguments. +func (p *parser) parseEvent(raw *rawEvent, ev *Event) error { + desc := &EventDescriptions[raw.typ] + if desc.Name == "" { + return fmt.Errorf("missing description for event type %d", raw.typ) + } + narg := raw.argNum() + if len(raw.args) != narg { + return fmt.Errorf("%s has wrong number of arguments: want %d, got %d", desc.Name, narg, len(raw.args)) + } + switch raw.typ { + case EvBatch: + p.lastGs[p.lastP] = p.lastG + if raw.args[0] != math.MaxUint64 && raw.args[0] > math.MaxInt32 { + return fmt.Errorf("processor ID %d is larger than maximum of %d", raw.args[0], uint64(math.MaxInt32)) + } + if raw.args[0] == math.MaxUint64 { + p.lastP = -1 + } else { + p.lastP = int32(raw.args[0]) + } + p.lastG = p.lastGs[p.lastP] + p.lastTs = Timestamp(raw.args[1]) + case EvFrequency: + p.ticksPerSec = int64(raw.args[0]) + if p.ticksPerSec <= 0 { + // The most likely cause for this is tick skew on different CPUs. + // For example, solaris/amd64 seems to have wildly different + // ticks on different CPUs. + return ErrTimeOrder + } + case EvTimerGoroutine: + p.timerGoids[raw.args[0]] = true + case EvStack: + if len(raw.args) < 2 { + return fmt.Errorf("EvStack has wrong number of arguments: want at least 2, got %d", len(raw.args)) + } + size := raw.args[1] + if size > 1000 { + return fmt.Errorf("EvStack has bad number of frames: %d", size) + } + want := 2 + 4*size + if uint64(len(raw.args)) != want { + return fmt.Errorf("EvStack has wrong number of arguments: want %d, got %d", want, len(raw.args)) + } + id := uint32(raw.args[0]) + if id != 0 && size > 0 { + stk := p.allocateStack(size) + for i := 0; i < int(size); i++ { + pc := raw.args[2+i*4+0] + fn := raw.args[2+i*4+1] + file := raw.args[2+i*4+2] + line := raw.args[2+i*4+3] + stk[i] = pc + + if _, ok := p.pcs[pc]; !ok { + p.pcs[pc] = Frame{PC: pc, Fn: fn, File: file, Line: int(line)} + } + } + p.stacks[id] = stk + } + case EvCPUSample: + // These events get parsed during the indexing step and don't strictly + // belong to the batch. + default: + *ev = Event{Type: raw.typ, P: p.lastP, G: p.lastG} + var argOffset int + ev.Ts = p.lastTs + Timestamp(raw.args[0]) + argOffset = 1 + p.lastTs = ev.Ts + for i := argOffset; i < narg; i++ { + if i == narg-1 && desc.Stack { + ev.StkID = uint32(raw.args[i]) + } else { + ev.Args[i-argOffset] = raw.args[i] + } + } + switch raw.typ { + case EvGoStart, EvGoStartLocal, EvGoStartLabel: + p.lastG = ev.Args[0] + ev.G = p.lastG + case EvGoEnd, EvGoStop, EvGoSched, EvGoPreempt, + EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv, + EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, + EvGoSysBlock, EvGoBlockGC: + p.lastG = 0 + case EvGoSysExit, EvGoWaiting, EvGoInSyscall: + ev.G = ev.Args[0] + case EvUserTaskCreate: + // e.Args 0: taskID, 1:parentID, 2:nameID + case EvUserRegion: + // e.Args 0: taskID, 1: mode, 2:nameID + case EvUserLog: + // e.Args 0: taskID, 1:keyID, 2: stackID, 3: messageID + // raw.sargs 0: message + + if id, ok := p.inlineStringsMapping[raw.sargs[0]]; ok { + ev.Args[3] = uint64(id) + } else { + id := len(p.inlineStrings) + p.inlineStringsMapping[raw.sargs[0]] = id + p.inlineStrings = append(p.inlineStrings, raw.sargs[0]) + ev.Args[3] = uint64(id) + } + } + + return nil + } + + ev.Type = EvNone + return nil +} + +// ErrTimeOrder is returned by Parse when the trace contains +// time stamps that do not respect actual event ordering. +var ErrTimeOrder = errors.New("time stamps out of order") + +// postProcessTrace does inter-event verification and information restoration. +// The resulting trace is guaranteed to be consistent +// (for example, a P does not run two Gs at the same time, or a G is indeed +// blocked before an unblock event). +func (p *parser) postProcessTrace(events Events) error { + const ( + gDead = iota + gRunnable + gRunning + gWaiting + ) + type gdesc struct { + state int + ev *Event + evStart *Event + evCreate *Event + evMarkAssist *Event + } + type pdesc struct { + running bool + g uint64 + evSweep *Event + } + + gs := make(map[uint64]gdesc) + ps := make(map[int32]pdesc) + tasks := make(map[uint64]*Event) // task id to task creation events + activeRegions := make(map[uint64][]*Event) // goroutine id to stack of regions + gs[0] = gdesc{state: gRunning} + var evGC, evSTW *Event + + checkRunning := func(p pdesc, g gdesc, ev *Event, allowG0 bool) error { + name := EventDescriptions[ev.Type].Name + if g.state != gRunning { + return fmt.Errorf("g %d is not running while %s (time %d)", ev.G, name, ev.Ts) + } + if p.g != ev.G { + return fmt.Errorf("p %d is not running g %d while %s (time %d)", ev.P, ev.G, name, ev.Ts) + } + if !allowG0 && ev.G == 0 { + return fmt.Errorf("g 0 did %s (time %d)", name, ev.Ts) + } + return nil + } + + for evIdx := 0; evIdx < events.Len(); evIdx++ { + ev := events.Ptr(evIdx) + + switch ev.Type { + case EvProcStart: + p := ps[ev.P] + if p.running { + return fmt.Errorf("p %d is running before start (time %d)", ev.P, ev.Ts) + } + p.running = true + + ps[ev.P] = p + case EvProcStop: + p := ps[ev.P] + if !p.running { + return fmt.Errorf("p %d is not running before stop (time %d)", ev.P, ev.Ts) + } + if p.g != 0 { + return fmt.Errorf("p %d is running a goroutine %d during stop (time %d)", ev.P, p.g, ev.Ts) + } + p.running = false + + ps[ev.P] = p + case EvGCStart: + if evGC != nil { + return fmt.Errorf("previous GC is not ended before a new one (time %d)", ev.Ts) + } + evGC = ev + // Attribute this to the global GC state. + ev.P = GCP + case EvGCDone: + if evGC == nil { + return fmt.Errorf("bogus GC end (time %d)", ev.Ts) + } + evGC = nil + case EvSTWStart: + evp := &evSTW + if *evp != nil { + return fmt.Errorf("previous STW is not ended before a new one (time %d)", ev.Ts) + } + *evp = ev + case EvSTWDone: + evp := &evSTW + if *evp == nil { + return fmt.Errorf("bogus STW end (time %d)", ev.Ts) + } + *evp = nil + case EvGCSweepStart: + p := ps[ev.P] + if p.evSweep != nil { + return fmt.Errorf("previous sweeping is not ended before a new one (time %d)", ev.Ts) + } + p.evSweep = ev + + ps[ev.P] = p + case EvGCMarkAssistStart: + g := gs[ev.G] + if g.evMarkAssist != nil { + return fmt.Errorf("previous mark assist is not ended before a new one (time %d)", ev.Ts) + } + g.evMarkAssist = ev + + gs[ev.G] = g + case EvGCMarkAssistDone: + // Unlike most events, mark assists can be in progress when a + // goroutine starts tracing, so we can't report an error here. + g := gs[ev.G] + if g.evMarkAssist != nil { + g.evMarkAssist = nil + } + + gs[ev.G] = g + case EvGCSweepDone: + p := ps[ev.P] + if p.evSweep == nil { + return fmt.Errorf("bogus sweeping end (time %d)", ev.Ts) + } + p.evSweep = nil + + ps[ev.P] = p + case EvGoWaiting: + g := gs[ev.G] + if g.state != gRunnable { + return fmt.Errorf("g %d is not runnable before EvGoWaiting (time %d)", ev.G, ev.Ts) + } + g.state = gWaiting + g.ev = ev + + gs[ev.G] = g + case EvGoInSyscall: + g := gs[ev.G] + if g.state != gRunnable { + return fmt.Errorf("g %d is not runnable before EvGoInSyscall (time %d)", ev.G, ev.Ts) + } + g.state = gWaiting + g.ev = ev + + gs[ev.G] = g + case EvGoCreate: + g := gs[ev.G] + p := ps[ev.P] + if err := checkRunning(p, g, ev, true); err != nil { + return err + } + if _, ok := gs[ev.Args[0]]; ok { + return fmt.Errorf("g %d already exists (time %d)", ev.Args[0], ev.Ts) + } + gs[ev.Args[0]] = gdesc{state: gRunnable, ev: ev, evCreate: ev} + + case EvGoStart, EvGoStartLabel: + g := gs[ev.G] + p := ps[ev.P] + if g.state != gRunnable { + return fmt.Errorf("g %d is not runnable before start (time %d)", ev.G, ev.Ts) + } + if p.g != 0 { + return fmt.Errorf("p %d is already running g %d while start g %d (time %d)", ev.P, p.g, ev.G, ev.Ts) + } + g.state = gRunning + g.evStart = ev + p.g = ev.G + if g.evCreate != nil { + ev.StkID = uint32(g.evCreate.Args[1]) + g.evCreate = nil + } + + if g.ev != nil { + g.ev = nil + } + + gs[ev.G] = g + ps[ev.P] = p + case EvGoEnd, EvGoStop: + g := gs[ev.G] + p := ps[ev.P] + if err := checkRunning(p, g, ev, false); err != nil { + return err + } + g.evStart = nil + g.state = gDead + p.g = 0 + + if ev.Type == EvGoEnd { // flush all active regions + delete(activeRegions, ev.G) + } + + gs[ev.G] = g + ps[ev.P] = p + case EvGoSched, EvGoPreempt: + g := gs[ev.G] + p := ps[ev.P] + if err := checkRunning(p, g, ev, false); err != nil { + return err + } + g.state = gRunnable + g.evStart = nil + p.g = 0 + g.ev = ev + + gs[ev.G] = g + ps[ev.P] = p + case EvGoUnblock: + g := gs[ev.G] + p := ps[ev.P] + if g.state != gRunning { + return fmt.Errorf("g %d is not running while unpark (time %d)", ev.G, ev.Ts) + } + if ev.P != TimerP && p.g != ev.G { + return fmt.Errorf("p %d is not running g %d while unpark (time %d)", ev.P, ev.G, ev.Ts) + } + g1 := gs[ev.Args[0]] + if g1.state != gWaiting { + return fmt.Errorf("g %d is not waiting before unpark (time %d)", ev.Args[0], ev.Ts) + } + if g1.ev != nil && g1.ev.Type == EvGoBlockNet { + ev.P = NetpollP + } + g1.state = gRunnable + g1.ev = ev + gs[ev.Args[0]] = g1 + + case EvGoSysCall: + g := gs[ev.G] + p := ps[ev.P] + if err := checkRunning(p, g, ev, false); err != nil { + return err + } + g.ev = ev + + gs[ev.G] = g + case EvGoSysBlock: + g := gs[ev.G] + p := ps[ev.P] + if err := checkRunning(p, g, ev, false); err != nil { + return err + } + g.state = gWaiting + g.evStart = nil + p.g = 0 + + gs[ev.G] = g + ps[ev.P] = p + case EvGoSysExit: + g := gs[ev.G] + if g.state != gWaiting { + return fmt.Errorf("g %d is not waiting during syscall exit (time %d)", ev.G, ev.Ts) + } + g.state = gRunnable + g.ev = ev + + gs[ev.G] = g + case EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv, + EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoBlockGC: + g := gs[ev.G] + p := ps[ev.P] + if err := checkRunning(p, g, ev, false); err != nil { + return err + } + g.state = gWaiting + g.ev = ev + g.evStart = nil + p.g = 0 + + gs[ev.G] = g + ps[ev.P] = p + case EvUserTaskCreate: + taskid := ev.Args[0] + if prevEv, ok := tasks[taskid]; ok { + return fmt.Errorf("task id conflicts (id:%d), %q vs %q", taskid, ev, prevEv) + } + tasks[ev.Args[0]] = ev + + case EvUserTaskEnd: + taskid := ev.Args[0] + delete(tasks, taskid) + + case EvUserRegion: + mode := ev.Args[1] + regions := activeRegions[ev.G] + if mode == 0 { // region start + activeRegions[ev.G] = append(regions, ev) // push + } else if mode == 1 { // region end + n := len(regions) + if n > 0 { // matching region start event is in the trace. + s := regions[n-1] + if s.Args[0] != ev.Args[0] || s.Args[2] != ev.Args[2] { // task id, region name mismatch + return fmt.Errorf("misuse of region in goroutine %d: span end %q when the inner-most active span start event is %q", ev.G, ev, s) + } + + if n > 1 { + activeRegions[ev.G] = regions[:n-1] + } else { + delete(activeRegions, ev.G) + } + } + } else { + return fmt.Errorf("invalid user region mode: %q", ev) + } + } + + if ev.StkID != 0 && len(p.stacks[ev.StkID]) == 0 { + // Make sure events don't refer to stacks that don't exist or to + // stacks with zero frames. Neither of these should be possible, but + // better be safe than sorry. + + ev.StkID = 0 + } + + } + + // TODO(mknyszek): restore stacks for EvGoStart events. + return nil +} + +var errMalformedVarint = errors.New("malformatted base-128 varint") + +// readVal reads unsigned base-128 value from r. +func (p *parser) readVal() (uint64, error) { + v, n := binary.Uvarint(p.data[p.off:]) + if n <= 0 { + return 0, errMalformedVarint + } + p.off += n + return v, nil +} + +func readValFrom(buf []byte) (v uint64, rem []byte, err error) { + v, n := binary.Uvarint(buf) + if n <= 0 { + return 0, nil, errMalformedVarint + } + return v, buf[n:], nil +} + +func (ev *Event) String() string { + desc := &EventDescriptions[ev.Type] + w := new(bytes.Buffer) + fmt.Fprintf(w, "%d %s p=%d g=%d stk=%d", ev.Ts, desc.Name, ev.P, ev.G, ev.StkID) + for i, a := range desc.Args { + fmt.Fprintf(w, " %s=%d", a, ev.Args[i]) + } + return w.String() +} + +// argNum returns total number of args for the event accounting for timestamps, +// sequence numbers and differences between trace format versions. +func (raw *rawEvent) argNum() int { + desc := &EventDescriptions[raw.typ] + if raw.typ == EvStack { + return len(raw.args) + } + narg := len(desc.Args) + if desc.Stack { + narg++ + } + switch raw.typ { + case EvBatch, EvFrequency, EvTimerGoroutine: + return narg + } + narg++ // timestamp + return narg +} + +// Event types in the trace. +// Verbatim copy from src/runtime/trace.go with the "trace" prefix removed. +const ( + EvNone event.Type = 0 // unused + EvBatch event.Type = 1 // start of per-P batch of events [pid, timestamp] + EvFrequency event.Type = 2 // contains tracer timer frequency [frequency (ticks per second)] + EvStack event.Type = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] + EvGomaxprocs event.Type = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] + EvProcStart event.Type = 5 // start of P [timestamp, thread id] + EvProcStop event.Type = 6 // stop of P [timestamp] + EvGCStart event.Type = 7 // GC start [timestamp, seq, stack id] + EvGCDone event.Type = 8 // GC done [timestamp] + EvSTWStart event.Type = 9 // GC mark termination start [timestamp, kind] + EvSTWDone event.Type = 10 // GC mark termination done [timestamp] + EvGCSweepStart event.Type = 11 // GC sweep start [timestamp, stack id] + EvGCSweepDone event.Type = 12 // GC sweep done [timestamp, swept, reclaimed] + EvGoCreate event.Type = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id] + EvGoStart event.Type = 14 // goroutine starts running [timestamp, goroutine id, seq] + EvGoEnd event.Type = 15 // goroutine ends [timestamp] + EvGoStop event.Type = 16 // goroutine stops (like in select{}) [timestamp, stack] + EvGoSched event.Type = 17 // goroutine calls Gosched [timestamp, stack] + EvGoPreempt event.Type = 18 // goroutine is preempted [timestamp, stack] + EvGoSleep event.Type = 19 // goroutine calls Sleep [timestamp, stack] + EvGoBlock event.Type = 20 // goroutine blocks [timestamp, stack] + EvGoUnblock event.Type = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack] + EvGoBlockSend event.Type = 22 // goroutine blocks on chan send [timestamp, stack] + EvGoBlockRecv event.Type = 23 // goroutine blocks on chan recv [timestamp, stack] + EvGoBlockSelect event.Type = 24 // goroutine blocks on select [timestamp, stack] + EvGoBlockSync event.Type = 25 // goroutine blocks on Mutex/RWMutex [timestamp, stack] + EvGoBlockCond event.Type = 26 // goroutine blocks on Cond [timestamp, stack] + EvGoBlockNet event.Type = 27 // goroutine blocks on network [timestamp, stack] + EvGoSysCall event.Type = 28 // syscall enter [timestamp, stack] + EvGoSysExit event.Type = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp] + EvGoSysBlock event.Type = 30 // syscall blocks [timestamp] + EvGoWaiting event.Type = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id] + EvGoInSyscall event.Type = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id] + EvHeapAlloc event.Type = 33 // gcController.heapLive change [timestamp, heap live bytes] + EvHeapGoal event.Type = 34 // gcController.heapGoal change [timestamp, heap goal bytes] + EvTimerGoroutine event.Type = 35 // denotes timer goroutine [timer goroutine id] + EvFutileWakeup event.Type = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] + EvString event.Type = 37 // string dictionary entry [ID, length, string] + EvGoStartLocal event.Type = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id] + EvGoUnblockLocal event.Type = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack] + EvGoSysExitLocal event.Type = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp] + EvGoStartLabel event.Type = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id] + EvGoBlockGC event.Type = 42 // goroutine blocks on GC assist [timestamp, stack] + EvGCMarkAssistStart event.Type = 43 // GC mark assist start [timestamp, stack] + EvGCMarkAssistDone event.Type = 44 // GC mark assist done [timestamp] + EvUserTaskCreate event.Type = 45 // trace.NewTask [timestamp, internal task id, internal parent id, stack, name string] + EvUserTaskEnd event.Type = 46 // end of task [timestamp, internal task id, stack] + EvUserRegion event.Type = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), name string] + EvUserLog event.Type = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] + EvCPUSample event.Type = 49 // CPU profiling sample [timestamp, stack, real timestamp, real P id (-1 when absent), goroutine id] + EvCount event.Type = 50 +) + +var EventDescriptions = [256]struct { + Name string + minVersion version.Version + Stack bool + Args []string + SArgs []string // string arguments +}{ + EvNone: {"None", 5, false, []string{}, nil}, + EvBatch: {"Batch", 5, false, []string{"p", "ticks"}, nil}, // in 1.5 format it was {"p", "seq", "ticks"} + EvFrequency: {"Frequency", 5, false, []string{"freq"}, nil}, // in 1.5 format it was {"freq", "unused"} + EvStack: {"Stack", 5, false, []string{"id", "siz"}, nil}, + EvGomaxprocs: {"Gomaxprocs", 5, true, []string{"procs"}, nil}, + EvProcStart: {"ProcStart", 5, false, []string{"thread"}, nil}, + EvProcStop: {"ProcStop", 5, false, []string{}, nil}, + EvGCStart: {"GCStart", 5, true, []string{"seq"}, nil}, // in 1.5 format it was {} + EvGCDone: {"GCDone", 5, false, []string{}, nil}, + EvSTWStart: {"GCSTWStart", 5, false, []string{"kindid"}, []string{"kind"}}, // <= 1.9, args was {} (implicitly {0}) + EvSTWDone: {"GCSTWDone", 5, false, []string{}, nil}, + EvGCSweepStart: {"GCSweepStart", 5, true, []string{}, nil}, + EvGCSweepDone: {"GCSweepDone", 5, false, []string{"swept", "reclaimed"}, nil}, // before 1.9, format was {} + EvGoCreate: {"GoCreate", 5, true, []string{"g", "stack"}, nil}, + EvGoStart: {"GoStart", 5, false, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} + EvGoEnd: {"GoEnd", 5, false, []string{}, nil}, + EvGoStop: {"GoStop", 5, true, []string{}, nil}, + EvGoSched: {"GoSched", 5, true, []string{}, nil}, + EvGoPreempt: {"GoPreempt", 5, true, []string{}, nil}, + EvGoSleep: {"GoSleep", 5, true, []string{}, nil}, + EvGoBlock: {"GoBlock", 5, true, []string{}, nil}, + EvGoUnblock: {"GoUnblock", 5, true, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} + EvGoBlockSend: {"GoBlockSend", 5, true, []string{}, nil}, + EvGoBlockRecv: {"GoBlockRecv", 5, true, []string{}, nil}, + EvGoBlockSelect: {"GoBlockSelect", 5, true, []string{}, nil}, + EvGoBlockSync: {"GoBlockSync", 5, true, []string{}, nil}, + EvGoBlockCond: {"GoBlockCond", 5, true, []string{}, nil}, + EvGoBlockNet: {"GoBlockNet", 5, true, []string{}, nil}, + EvGoSysCall: {"GoSysCall", 5, true, []string{}, nil}, + EvGoSysExit: {"GoSysExit", 5, false, []string{"g", "seq", "ts"}, nil}, + EvGoSysBlock: {"GoSysBlock", 5, false, []string{}, nil}, + EvGoWaiting: {"GoWaiting", 5, false, []string{"g"}, nil}, + EvGoInSyscall: {"GoInSyscall", 5, false, []string{"g"}, nil}, + EvHeapAlloc: {"HeapAlloc", 5, false, []string{"mem"}, nil}, + EvHeapGoal: {"HeapGoal", 5, false, []string{"mem"}, nil}, + EvTimerGoroutine: {"TimerGoroutine", 5, false, []string{"g"}, nil}, // in 1.5 format it was {"g", "unused"} + EvFutileWakeup: {"FutileWakeup", 5, false, []string{}, nil}, + EvString: {"String", 7, false, []string{}, nil}, + EvGoStartLocal: {"GoStartLocal", 7, false, []string{"g"}, nil}, + EvGoUnblockLocal: {"GoUnblockLocal", 7, true, []string{"g"}, nil}, + EvGoSysExitLocal: {"GoSysExitLocal", 7, false, []string{"g", "ts"}, nil}, + EvGoStartLabel: {"GoStartLabel", 8, false, []string{"g", "seq", "labelid"}, []string{"label"}}, + EvGoBlockGC: {"GoBlockGC", 8, true, []string{}, nil}, + EvGCMarkAssistStart: {"GCMarkAssistStart", 9, true, []string{}, nil}, + EvGCMarkAssistDone: {"GCMarkAssistDone", 9, false, []string{}, nil}, + EvUserTaskCreate: {"UserTaskCreate", 11, true, []string{"taskid", "pid", "typeid"}, []string{"name"}}, + EvUserTaskEnd: {"UserTaskEnd", 11, true, []string{"taskid"}, nil}, + EvUserRegion: {"UserRegion", 11, true, []string{"taskid", "mode", "typeid"}, []string{"name"}}, + EvUserLog: {"UserLog", 11, true, []string{"id", "keyid"}, []string{"category", "message"}}, + EvCPUSample: {"CPUSample", 19, true, []string{"ts", "p", "g"}, nil}, +} + +//gcassert:inline +func (p *parser) allocateStack(size uint64) []uint64 { + if size == 0 { + return nil + } + + // Stacks are plentiful but small. For our "Staticcheck on std" trace with + // 11e6 events, we have roughly 500,000 stacks, using 200 MiB of memory. To + // avoid making 500,000 small allocations we allocate backing arrays 1 MiB + // at a time. + out := p.stacksData + if uint64(len(out)) < size { + out = make([]uint64, 1024*128) + } + p.stacksData = out[size:] + return out[:size:size] +} + +func (tr *Trace) STWReason(kindID uint64) STWReason { + if tr.Version < 21 { + if kindID == 0 || kindID == 1 { + return STWReason(kindID + 1) + } else { + return STWUnknown + } + } else if tr.Version == 21 { + if kindID < NumSTWReasons { + return STWReason(kindID) + } else { + return STWUnknown + } + } else { + return STWUnknown + } +} + +type STWReason int + +const ( + STWUnknown STWReason = 0 + STWGCMarkTermination STWReason = 1 + STWGCSweepTermination STWReason = 2 + STWWriteHeapDump STWReason = 3 + STWGoroutineProfile STWReason = 4 + STWGoroutineProfileCleanup STWReason = 5 + STWAllGoroutinesStackTrace STWReason = 6 + STWReadMemStats STWReason = 7 + STWAllThreadsSyscall STWReason = 8 + STWGOMAXPROCS STWReason = 9 + STWStartTrace STWReason = 10 + STWStopTrace STWReason = 11 + STWCountPagesInUse STWReason = 12 + STWReadMetricsSlow STWReason = 13 + STWReadMemStatsSlow STWReason = 14 + STWPageCachePagesLeaked STWReason = 15 + STWResetDebugLog STWReason = 16 + + NumSTWReasons = 17 +) diff --git a/src/internal/trace/internal/oldtrace/parser_test.go b/src/internal/trace/internal/oldtrace/parser_test.go new file mode 100644 index 0000000000..6fe31e2e7e --- /dev/null +++ b/src/internal/trace/internal/oldtrace/parser_test.go @@ -0,0 +1,151 @@ +// Copyright 2015 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 oldtrace + +import ( + "bytes" + "internal/trace/version" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestCorruptedInputs(t *testing.T) { + // These inputs crashed parser previously. + tests := []string{ + "gotrace\x00\x020", + "gotrace\x00Q00\x020", + "gotrace\x00T00\x020", + "gotrace\x00\xc3\x0200", + "go 1.5 trace\x00\x00\x00\x00\x020", + "go 1.5 trace\x00\x00\x00\x00Q00\x020", + "go 1.5 trace\x00\x00\x00\x00T00\x020", + "go 1.5 trace\x00\x00\x00\x00\xc3\x0200", + } + for _, data := range tests { + res, err := Parse(strings.NewReader(data), 5) + if err == nil || res.Events.Len() != 0 || res.Stacks != nil { + t.Fatalf("no error on input: %q", data) + } + } +} + +func TestParseCanned(t *testing.T) { + files, err := os.ReadDir("./testdata") + if err != nil { + t.Fatalf("failed to read ./testdata: %v", err) + } + for _, f := range files { + info, err := f.Info() + if err != nil { + t.Fatal(err) + } + if testing.Short() && info.Size() > 10000 { + continue + } + name := filepath.Join("./testdata", f.Name()) + data, err := os.ReadFile(name) + if err != nil { + t.Fatal(err) + } + r := bytes.NewReader(data) + v, err := version.ReadHeader(r) + if err != nil { + t.Errorf("failed to parse good trace %s: %s", f.Name(), err) + } + trace, err := Parse(r, v) + switch { + case strings.HasSuffix(f.Name(), "_good"): + if err != nil { + t.Errorf("failed to parse good trace %v: %v", f.Name(), err) + } + checkTrace(t, int(v), trace) + case strings.HasSuffix(f.Name(), "_unordered"): + if err != ErrTimeOrder { + t.Errorf("unordered trace is not detected %v: %v", f.Name(), err) + } + default: + t.Errorf("unknown input file suffix: %v", f.Name()) + } + } +} + +// checkTrace walks over a good trace and makes a bunch of additional checks +// that may not cause the parser to outright fail. +func checkTrace(t *testing.T, ver int, res Trace) { + for i := 0; i < res.Events.Len(); i++ { + ev := res.Events.Ptr(i) + if ver >= 21 { + if ev.Type == EvSTWStart && res.Strings[ev.Args[0]] == "unknown" { + t.Errorf("found unknown STW event; update stwReasonStrings?") + } + } + } +} + +func TestBuckets(t *testing.T) { + var evs Events + + const N = eventsBucketSize*3 + 123 + for i := 0; i < N; i++ { + evs.append(Event{Ts: Timestamp(i)}) + } + + if n := len(evs.buckets); n != 4 { + t.Fatalf("got %d buckets, want %d", n, 4) + } + + if n := evs.Len(); n != N { + t.Fatalf("got %d events, want %d", n, N) + } + + var n int + evs.All()(func(ev *Event) bool { + n++ + return true + }) + if n != N { + t.Fatalf("iterated over %d events, expected %d", n, N) + } + + const consume = eventsBucketSize + 50 + for i := 0; i < consume; i++ { + if _, ok := evs.Pop(); !ok { + t.Fatalf("iteration failed after %d events", i) + } + } + + if evs.buckets[0] != nil { + t.Fatalf("expected first bucket to have been dropped") + } + for i, b := range evs.buckets[1:] { + if b == nil { + t.Fatalf("expected bucket %d to be non-nil", i+1) + } + } + + if n := evs.Len(); n != N-consume { + t.Fatalf("got %d remaining elements, expected %d", n, N-consume) + } + + ev := evs.Ptr(0) + if ev.Ts != consume { + t.Fatalf("got event %d, expected %d", int(ev.Ts), consume) + } + + for { + _, ok := evs.Pop() + if !ok { + break + } + } + + for i, b := range evs.buckets { + if b != nil { + t.Fatalf("expected bucket %d to be nil", i) + } + } +} diff --git a/src/internal/trace/internal/oldtrace/testdata/fmt_1_21_pprof_good b/src/internal/trace/internal/oldtrace/testdata/fmt_1_21_pprof_good new file mode 100644 index 0000000000..f3ea402fdb Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/fmt_1_21_pprof_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/http_1_19_good b/src/internal/trace/internal/oldtrace/testdata/http_1_19_good new file mode 100644 index 0000000000..c1d519e7d5 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/http_1_19_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/http_1_21_good b/src/internal/trace/internal/oldtrace/testdata/http_1_21_good new file mode 100644 index 0000000000..b3295f9e5d Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/http_1_21_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_1_11_good b/src/internal/trace/internal/oldtrace/testdata/stress_1_11_good new file mode 100644 index 0000000000..6468d89290 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/stress_1_11_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_1_19_good b/src/internal/trace/internal/oldtrace/testdata/stress_1_19_good new file mode 100644 index 0000000000..13f59268e6 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/stress_1_19_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_1_21_good b/src/internal/trace/internal/oldtrace/testdata/stress_1_21_good new file mode 100644 index 0000000000..1ade5e0eb6 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/stress_1_21_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_11_good b/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_11_good new file mode 100644 index 0000000000..457f01a6cd Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_11_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_19_good b/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_19_good new file mode 100644 index 0000000000..92d92789c4 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_19_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_21_good b/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_21_good new file mode 100644 index 0000000000..fff46a9a07 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_21_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_11_good b/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_11_good new file mode 100644 index 0000000000..f4edb67e65 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_11_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_19_good b/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_19_good new file mode 100644 index 0000000000..1daa3b25a6 Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_19_good differ diff --git a/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_21_good b/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_21_good new file mode 100644 index 0000000000..5c01a6405d Binary files /dev/null and b/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_21_good differ diff --git a/src/internal/trace/internal/testgen/go122/trace.go b/src/internal/trace/internal/testgen/go122/trace.go new file mode 100644 index 0000000000..5fd995ae9a --- /dev/null +++ b/src/internal/trace/internal/testgen/go122/trace.go @@ -0,0 +1,401 @@ +// 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. + +package testkit + +import ( + "bytes" + "encoding/binary" + "fmt" + "os" + "regexp" + "strings" + + "internal/trace" + "internal/trace/event" + "internal/trace/event/go122" + "internal/trace/raw" + "internal/trace/version" + "internal/txtar" +) + +func Main(f func(*Trace)) { + // Create an output file. + out, err := os.Create(os.Args[1]) + if err != nil { + panic(err.Error()) + } + defer out.Close() + + // Create a new trace. + trace := NewTrace() + + // Call the generator. + f(trace) + + // Write out the generator's state. + if _, err := out.Write(trace.Generate()); err != nil { + panic(err.Error()) + } +} + +// Trace represents an execution trace for testing. +// +// It does a little bit of work to ensure that the produced trace is valid, +// just for convenience. It mainly tracks batches and batch sizes (so they're +// trivially correct), tracks strings and stacks, and makes sure emitted string +// and stack batches are valid. That last part can be controlled by a few options. +// +// Otherwise, it performs no validation on the trace at all. +type Trace struct { + // Trace data state. + ver version.Version + names map[string]event.Type + specs []event.Spec + events []raw.Event + gens []*Generation + validTimestamps bool + + // Expectation state. + bad bool + badMatch *regexp.Regexp +} + +// NewTrace creates a new trace. +func NewTrace() *Trace { + ver := version.Go122 + return &Trace{ + names: event.Names(ver.Specs()), + specs: ver.Specs(), + validTimestamps: true, + } +} + +// ExpectFailure writes down that the trace should be broken. The caller +// must provide a pattern matching the expected error produced by the parser. +func (t *Trace) ExpectFailure(pattern string) { + t.bad = true + t.badMatch = regexp.MustCompile(pattern) +} + +// ExpectSuccess writes down that the trace should successfully parse. +func (t *Trace) ExpectSuccess() { + t.bad = false +} + +// RawEvent emits an event into the trace. name must correspond to one +// of the names in Specs() result for the version that was passed to +// this trace. +func (t *Trace) RawEvent(typ event.Type, data []byte, args ...uint64) { + t.events = append(t.events, t.createEvent(typ, data, args...)) +} + +// DisableTimestamps makes the timestamps for all events generated after +// this call zero. Raw events are exempted from this because the caller +// has to pass their own timestamp into those events anyway. +func (t *Trace) DisableTimestamps() { + t.validTimestamps = false +} + +// Generation creates a new trace generation. +// +// This provides more structure than Event to allow for more easily +// creating complex traces that are mostly or completely correct. +func (t *Trace) Generation(gen uint64) *Generation { + g := &Generation{ + trace: t, + gen: gen, + strings: make(map[string]uint64), + stacks: make(map[stack]uint64), + } + t.gens = append(t.gens, g) + return g +} + +// Generate creates a test file for the trace. +func (t *Trace) Generate() []byte { + // Trace file contents. + var buf bytes.Buffer + tw, err := raw.NewTextWriter(&buf, version.Go122) + if err != nil { + panic(err.Error()) + } + + // Write raw top-level events. + for _, e := range t.events { + tw.WriteEvent(e) + } + + // Write generations. + for _, g := range t.gens { + g.writeEventsTo(tw) + } + + // Expectation file contents. + expect := []byte("SUCCESS\n") + if t.bad { + expect = []byte(fmt.Sprintf("FAILURE %q\n", t.badMatch)) + } + + // Create the test file's contents. + return txtar.Format(&txtar.Archive{ + Files: []txtar.File{ + {Name: "expect", Data: expect}, + {Name: "trace", Data: buf.Bytes()}, + }, + }) +} + +func (t *Trace) createEvent(ev event.Type, data []byte, args ...uint64) raw.Event { + spec := t.specs[ev] + if ev != go122.EvStack { + if arity := len(spec.Args); len(args) != arity { + panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args))) + } + } + return raw.Event{ + Version: version.Go122, + Ev: ev, + Args: args, + Data: data, + } +} + +type stack struct { + stk [32]trace.StackFrame + len int +} + +var ( + NoString = "" + NoStack = []trace.StackFrame{} +) + +// Generation represents a single generation in the trace. +type Generation struct { + trace *Trace + gen uint64 + batches []*Batch + strings map[string]uint64 + stacks map[stack]uint64 + + // Options applied when Trace.Generate is called. + ignoreStringBatchSizeLimit bool + ignoreStackBatchSizeLimit bool +} + +// Batch starts a new event batch in the trace data. +// +// This is convenience function for generating correct batches. +func (g *Generation) Batch(thread trace.ThreadID, time Time) *Batch { + if !g.trace.validTimestamps { + time = 0 + } + b := &Batch{ + gen: g, + thread: thread, + timestamp: time, + } + g.batches = append(g.batches, b) + return b +} + +// String registers a string with the trace. +// +// This is a convenience function for easily adding correct +// strings to traces. +func (g *Generation) String(s string) uint64 { + if len(s) == 0 { + return 0 + } + if id, ok := g.strings[s]; ok { + return id + } + id := uint64(len(g.strings) + 1) + g.strings[s] = id + return id +} + +// Stack registers a stack with the trace. +// +// This is a convenience function for easily adding correct +// stacks to traces. +func (g *Generation) Stack(stk []trace.StackFrame) uint64 { + if len(stk) == 0 { + return 0 + } + if len(stk) > 32 { + panic("stack too big for test") + } + var stkc stack + copy(stkc.stk[:], stk) + stkc.len = len(stk) + if id, ok := g.stacks[stkc]; ok { + return id + } + id := uint64(len(g.stacks) + 1) + g.stacks[stkc] = id + return id +} + +// writeEventsTo emits event batches in the generation to tw. +func (g *Generation) writeEventsTo(tw *raw.TextWriter) { + // Write event batches for the generation. + for _, b := range g.batches { + b.writeEventsTo(tw) + } + + // Write frequency. + b := g.newStructuralBatch() + b.RawEvent(go122.EvFrequency, nil, 15625000) + b.writeEventsTo(tw) + + // Write stacks. + b = g.newStructuralBatch() + b.RawEvent(go122.EvStacks, nil) + for stk, id := range g.stacks { + stk := stk.stk[:stk.len] + args := []uint64{id} + for _, f := range stk { + args = append(args, f.PC, g.String(f.Func), g.String(f.File), f.Line) + } + b.RawEvent(go122.EvStack, nil, args...) + + // Flush the batch if necessary. + if !g.ignoreStackBatchSizeLimit && b.size > go122.MaxBatchSize/2 { + b.writeEventsTo(tw) + b = g.newStructuralBatch() + } + } + b.writeEventsTo(tw) + + // Write strings. + b = g.newStructuralBatch() + b.RawEvent(go122.EvStrings, nil) + for s, id := range g.strings { + b.RawEvent(go122.EvString, []byte(s), id) + + // Flush the batch if necessary. + if !g.ignoreStringBatchSizeLimit && b.size > go122.MaxBatchSize/2 { + b.writeEventsTo(tw) + b = g.newStructuralBatch() + } + } + b.writeEventsTo(tw) +} + +func (g *Generation) newStructuralBatch() *Batch { + return &Batch{gen: g, thread: trace.NoThread} +} + +// Batch represents an event batch. +type Batch struct { + gen *Generation + thread trace.ThreadID + timestamp Time + size uint64 + events []raw.Event +} + +// Event emits an event into a batch. name must correspond to one +// of the names in Specs() result for the version that was passed to +// this trace. Callers must omit the timestamp delta. +func (b *Batch) Event(name string, args ...any) { + ev, ok := b.gen.trace.names[name] + if !ok { + panic(fmt.Sprintf("invalid or unknown event %s", name)) + } + var uintArgs []uint64 + argOff := 0 + if b.gen.trace.specs[ev].IsTimedEvent { + if b.gen.trace.validTimestamps { + uintArgs = []uint64{1} + } else { + uintArgs = []uint64{0} + } + argOff = 1 + } + spec := b.gen.trace.specs[ev] + if arity := len(spec.Args) - argOff; len(args) != arity { + panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args))) + } + for i, arg := range args { + uintArgs = append(uintArgs, b.uintArgFor(arg, spec.Args[i+argOff])) + } + b.RawEvent(ev, nil, uintArgs...) +} + +func (b *Batch) uintArgFor(arg any, argSpec string) uint64 { + components := strings.SplitN(argSpec, "_", 2) + typStr := components[0] + if len(components) == 2 { + typStr = components[1] + } + var u uint64 + switch typStr { + case "value": + u = arg.(uint64) + case "stack": + u = b.gen.Stack(arg.([]trace.StackFrame)) + case "seq": + u = uint64(arg.(Seq)) + case "pstatus": + u = uint64(arg.(go122.ProcStatus)) + case "gstatus": + u = uint64(arg.(go122.GoStatus)) + case "g": + u = uint64(arg.(trace.GoID)) + case "m": + u = uint64(arg.(trace.ThreadID)) + case "p": + u = uint64(arg.(trace.ProcID)) + case "string": + u = b.gen.String(arg.(string)) + case "task": + u = uint64(arg.(trace.TaskID)) + default: + panic(fmt.Sprintf("unsupported arg type %q for spec %q", typStr, argSpec)) + } + return u +} + +// RawEvent emits an event into a batch. name must correspond to one +// of the names in Specs() result for the version that was passed to +// this trace. +func (b *Batch) RawEvent(typ event.Type, data []byte, args ...uint64) { + ev := b.gen.trace.createEvent(typ, data, args...) + + // Compute the size of the event and add it to the batch. + b.size += 1 // One byte for the event header. + var buf [binary.MaxVarintLen64]byte + for _, arg := range args { + b.size += uint64(binary.PutUvarint(buf[:], arg)) + } + if len(data) != 0 { + b.size += uint64(binary.PutUvarint(buf[:], uint64(len(data)))) + b.size += uint64(len(data)) + } + + // Add the event. + b.events = append(b.events, ev) +} + +// writeEventsTo emits events in the batch, including the batch header, to tw. +func (b *Batch) writeEventsTo(tw *raw.TextWriter) { + tw.WriteEvent(raw.Event{ + Version: version.Go122, + Ev: go122.EvEventBatch, + Args: []uint64{b.gen.gen, uint64(b.thread), uint64(b.timestamp), b.size}, + }) + for _, e := range b.events { + tw.WriteEvent(e) + } +} + +// Seq represents a sequence counter. +type Seq uint64 + +// Time represents a low-level trace timestamp (which does not necessarily +// correspond to nanoseconds, like trace.Time does). +type Time uint64 diff --git a/src/internal/trace/mud_test.go b/src/internal/trace/mud_test.go index b3d74dcf34..9856442cff 100644 --- a/src/internal/trace/mud_test.go +++ b/src/internal/trace/mud_test.go @@ -5,6 +5,7 @@ package trace import ( + "math" "math/rand" "testing" ) @@ -85,3 +86,15 @@ func TestMUDTracking(t *testing.T) { } } } + +// aeq returns true if x and y are equal up to 8 digits (1 part in 100 +// million). +// TODO(amedee) dup of gc_test.go +func aeq(x, y float64) bool { + if x < 0 && y < 0 { + x, y = -x, -y + } + const digits = 8 + factor := 1 - math.Pow(10, -digits+1) + return x*factor <= y && y*factor <= x +} diff --git a/src/internal/trace/oldtrace.go b/src/internal/trace/oldtrace.go new file mode 100644 index 0000000000..c49f8c7474 --- /dev/null +++ b/src/internal/trace/oldtrace.go @@ -0,0 +1,568 @@ +// Copyright 2024 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. + +// This file implements conversion from old (Go 1.11–Go 1.21) traces to the Go +// 1.22 format. +// +// Most events have direct equivalents in 1.22, at worst requiring arguments to +// be reordered. Some events, such as GoWaiting need to look ahead for follow-up +// events to determine the correct translation. GoSyscall, which is an +// instantaneous event, gets turned into a 1 ns long pair of +// GoSyscallStart+GoSyscallEnd, unless we observe a GoSysBlock, in which case we +// emit a GoSyscallStart+GoSyscallEndBlocked pair with the correct duration +// (i.e. starting at the original GoSyscall). +// +// The resulting trace treats the old trace as a single, large generation, +// sharing a single evTable for all events. +// +// We use a new (compared to what was used for 'go tool trace' in earlier +// versions of Go) parser for old traces that is optimized for speed, low memory +// usage, and minimal GC pressure. It allocates events in batches so that even +// though we have to load the entire trace into memory, the conversion process +// shouldn't result in a doubling of memory usage, even if all converted events +// are kept alive, as we free batches once we're done with them. +// +// The conversion process is lossless. + +package trace + +import ( + "errors" + "fmt" + "internal/trace/event" + "internal/trace/event/go122" + "internal/trace/internal/oldtrace" + "io" +) + +type oldTraceConverter struct { + trace oldtrace.Trace + evt *evTable + preInit bool + createdPreInit map[GoID]struct{} + events oldtrace.Events + extra []Event + extraArr [3]Event + tasks map[TaskID]taskState + seenProcs map[ProcID]struct{} + lastTs Time + procMs map[ProcID]ThreadID + lastStwReason uint64 + + inlineToStringID []uint64 + builtinToStringID []uint64 +} + +const ( + // Block reasons + sForever = iota + sPreempted + sGosched + sSleep + sChanSend + sChanRecv + sNetwork + sSync + sSyncCond + sSelect + sEmpty + sMarkAssistWait + + // STW kinds + sSTWUnknown + sSTWGCMarkTermination + sSTWGCSweepTermination + sSTWWriteHeapDump + sSTWGoroutineProfile + sSTWGoroutineProfileCleanup + sSTWAllGoroutinesStackTrace + sSTWReadMemStats + sSTWAllThreadsSyscall + sSTWGOMAXPROCS + sSTWStartTrace + sSTWStopTrace + sSTWCountPagesInUse + sSTWReadMetricsSlow + sSTWReadMemStatsSlow + sSTWPageCachePagesLeaked + sSTWResetDebugLog + + sLast +) + +func (it *oldTraceConverter) init(pr oldtrace.Trace) error { + it.trace = pr + it.preInit = true + it.createdPreInit = make(map[GoID]struct{}) + it.evt = &evTable{pcs: make(map[uint64]frame)} + it.events = pr.Events + it.extra = it.extraArr[:0] + it.tasks = make(map[TaskID]taskState) + it.seenProcs = make(map[ProcID]struct{}) + it.procMs = make(map[ProcID]ThreadID) + it.lastTs = -1 + + evt := it.evt + + // Convert from oldtracer's Strings map to our dataTable. + var max uint64 + for id, s := range pr.Strings { + evt.strings.insert(stringID(id), s) + if id > max { + max = id + } + } + pr.Strings = nil + + // Add all strings used for UserLog. In the old trace format, these were + // stored inline and didn't have IDs. We generate IDs for them. + if max+uint64(len(pr.InlineStrings)) < max { + return errors.New("trace contains too many strings") + } + var addErr error + add := func(id stringID, s string) { + if err := evt.strings.insert(id, s); err != nil && addErr == nil { + addErr = err + } + } + for id, s := range pr.InlineStrings { + nid := max + 1 + uint64(id) + it.inlineToStringID = append(it.inlineToStringID, nid) + add(stringID(nid), s) + } + max += uint64(len(pr.InlineStrings)) + pr.InlineStrings = nil + + // Add strings that the converter emits explicitly. + if max+uint64(sLast) < max { + return errors.New("trace contains too many strings") + } + it.builtinToStringID = make([]uint64, sLast) + addBuiltin := func(c int, s string) { + nid := max + 1 + uint64(c) + it.builtinToStringID[c] = nid + add(stringID(nid), s) + } + addBuiltin(sForever, "forever") + addBuiltin(sPreempted, "preempted") + addBuiltin(sGosched, "runtime.Gosched") + addBuiltin(sSleep, "sleep") + addBuiltin(sChanSend, "chan send") + addBuiltin(sChanRecv, "chan receive") + addBuiltin(sNetwork, "network") + addBuiltin(sSync, "sync") + addBuiltin(sSyncCond, "sync.(*Cond).Wait") + addBuiltin(sSelect, "select") + addBuiltin(sEmpty, "") + addBuiltin(sMarkAssistWait, "GC mark assist wait for work") + addBuiltin(sSTWUnknown, "") + addBuiltin(sSTWGCMarkTermination, "GC mark termination") + addBuiltin(sSTWGCSweepTermination, "GC sweep termination") + addBuiltin(sSTWWriteHeapDump, "write heap dump") + addBuiltin(sSTWGoroutineProfile, "goroutine profile") + addBuiltin(sSTWGoroutineProfileCleanup, "goroutine profile cleanup") + addBuiltin(sSTWAllGoroutinesStackTrace, "all goroutine stack trace") + addBuiltin(sSTWReadMemStats, "read mem stats") + addBuiltin(sSTWAllThreadsSyscall, "AllThreadsSyscall") + addBuiltin(sSTWGOMAXPROCS, "GOMAXPROCS") + addBuiltin(sSTWStartTrace, "start trace") + addBuiltin(sSTWStopTrace, "stop trace") + addBuiltin(sSTWCountPagesInUse, "CountPagesInUse (test)") + addBuiltin(sSTWReadMetricsSlow, "ReadMetricsSlow (test)") + addBuiltin(sSTWReadMemStatsSlow, "ReadMemStatsSlow (test)") + addBuiltin(sSTWPageCachePagesLeaked, "PageCachePagesLeaked (test)") + addBuiltin(sSTWResetDebugLog, "ResetDebugLog (test)") + + if addErr != nil { + // This should be impossible but let's be safe. + return fmt.Errorf("couldn't add strings: %w", addErr) + } + + it.evt.strings.compactify() + + // Convert stacks. + for id, stk := range pr.Stacks { + evt.stacks.insert(stackID(id), stack{pcs: stk}) + } + + // OPT(dh): if we could share the frame type between this package and + // oldtrace we wouldn't have to copy the map. + for pc, f := range pr.PCs { + evt.pcs[pc] = frame{ + pc: pc, + funcID: stringID(f.Fn), + fileID: stringID(f.File), + line: uint64(f.Line), + } + } + pr.Stacks = nil + pr.PCs = nil + evt.stacks.compactify() + return nil +} + +// next returns the next event, io.EOF if there are no more events, or a +// descriptive error for invalid events. +func (it *oldTraceConverter) next() (Event, error) { + if len(it.extra) > 0 { + ev := it.extra[0] + it.extra = it.extra[1:] + + if len(it.extra) == 0 { + it.extra = it.extraArr[:0] + } + // Two events aren't allowed to fall on the same timestamp in the new API, + // but this may happen when we produce EvGoStatus events + if ev.base.time <= it.lastTs { + ev.base.time = it.lastTs + 1 + } + it.lastTs = ev.base.time + return ev, nil + } + + oev, ok := it.events.Pop() + if !ok { + return Event{}, io.EOF + } + + ev, err := it.convertEvent(oev) + + if err == errSkip { + return it.next() + } else if err != nil { + return Event{}, err + } + + // Two events aren't allowed to fall on the same timestamp in the new API, + // but this may happen when we produce EvGoStatus events + if ev.base.time <= it.lastTs { + ev.base.time = it.lastTs + 1 + } + it.lastTs = ev.base.time + return ev, nil +} + +var errSkip = errors.New("skip event") + +// convertEvent converts an event from the old trace format to zero or more +// events in the new format. Most events translate 1 to 1. Some events don't +// result in an event right away, in which case convertEvent returns errSkip. +// Some events result in more than one new event; in this case, convertEvent +// returns the first event and stores additional events in it.extra. When +// encountering events that oldtrace shouldn't be able to emit, ocnvertEvent +// returns a descriptive error. +func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR error) { + var mappedType event.Type + var mappedArgs timedEventArgs + copy(mappedArgs[:], ev.Args[:]) + + switch ev.Type { + case oldtrace.EvGomaxprocs: + mappedType = go122.EvProcsChange + if it.preInit { + // The first EvGomaxprocs signals the end of trace initialization. At this point we've seen + // all goroutines that already existed at trace begin. + it.preInit = false + for gid := range it.createdPreInit { + // These are goroutines that already existed when tracing started but for which we + // received neither GoWaiting, GoInSyscall, or GoStart. These are goroutines that are in + // the states _Gidle or _Grunnable. + it.extra = append(it.extra, Event{ + ctx: schedCtx{ + // G: GoID(gid), + G: NoGoroutine, + P: NoProc, + M: NoThread, + }, + table: it.evt, + base: baseEvent{ + typ: go122.EvGoStatus, + time: Time(ev.Ts), + args: timedEventArgs{uint64(gid), ^uint64(0), uint64(go122.GoRunnable)}, + }, + }) + } + it.createdPreInit = nil + return Event{}, errSkip + } + case oldtrace.EvProcStart: + it.procMs[ProcID(ev.P)] = ThreadID(ev.Args[0]) + if _, ok := it.seenProcs[ProcID(ev.P)]; ok { + mappedType = go122.EvProcStart + mappedArgs = timedEventArgs{uint64(ev.P)} + } else { + it.seenProcs[ProcID(ev.P)] = struct{}{} + mappedType = go122.EvProcStatus + mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcRunning)} + } + case oldtrace.EvProcStop: + if _, ok := it.seenProcs[ProcID(ev.P)]; ok { + mappedType = go122.EvProcStop + mappedArgs = timedEventArgs{uint64(ev.P)} + } else { + it.seenProcs[ProcID(ev.P)] = struct{}{} + mappedType = go122.EvProcStatus + mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcIdle)} + } + case oldtrace.EvGCStart: + mappedType = go122.EvGCBegin + case oldtrace.EvGCDone: + mappedType = go122.EvGCEnd + case oldtrace.EvSTWStart: + sid := it.builtinToStringID[sSTWUnknown+it.trace.STWReason(ev.Args[0])] + it.lastStwReason = sid + mappedType = go122.EvSTWBegin + mappedArgs = timedEventArgs{uint64(sid)} + case oldtrace.EvSTWDone: + mappedType = go122.EvSTWEnd + mappedArgs = timedEventArgs{it.lastStwReason} + case oldtrace.EvGCSweepStart: + mappedType = go122.EvGCSweepBegin + case oldtrace.EvGCSweepDone: + mappedType = go122.EvGCSweepEnd + case oldtrace.EvGoCreate: + if it.preInit { + it.createdPreInit[GoID(ev.Args[0])] = struct{}{} + return Event{}, errSkip + } + mappedType = go122.EvGoCreate + case oldtrace.EvGoStart: + if it.preInit { + mappedType = go122.EvGoStatus + mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoRunning)} + delete(it.createdPreInit, GoID(ev.Args[0])) + } else { + mappedType = go122.EvGoStart + } + case oldtrace.EvGoStartLabel: + it.extra = []Event{{ + ctx: schedCtx{ + G: GoID(ev.G), + P: ProcID(ev.P), + M: it.procMs[ProcID(ev.P)], + }, + table: it.evt, + base: baseEvent{ + typ: go122.EvGoLabel, + time: Time(ev.Ts), + args: timedEventArgs{ev.Args[2]}, + }, + }} + return Event{ + ctx: schedCtx{ + G: GoID(ev.G), + P: ProcID(ev.P), + M: it.procMs[ProcID(ev.P)], + }, + table: it.evt, + base: baseEvent{ + typ: go122.EvGoStart, + time: Time(ev.Ts), + args: mappedArgs, + }, + }, nil + case oldtrace.EvGoEnd: + mappedType = go122.EvGoDestroy + case oldtrace.EvGoStop: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sForever]), uint64(ev.StkID)} + case oldtrace.EvGoSched: + mappedType = go122.EvGoStop + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sGosched]), uint64(ev.StkID)} + case oldtrace.EvGoPreempt: + mappedType = go122.EvGoStop + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sPreempted]), uint64(ev.StkID)} + case oldtrace.EvGoSleep: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSleep]), uint64(ev.StkID)} + case oldtrace.EvGoBlock: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sEmpty]), uint64(ev.StkID)} + case oldtrace.EvGoUnblock: + mappedType = go122.EvGoUnblock + case oldtrace.EvGoBlockSend: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanSend]), uint64(ev.StkID)} + case oldtrace.EvGoBlockRecv: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanRecv]), uint64(ev.StkID)} + case oldtrace.EvGoBlockSelect: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSelect]), uint64(ev.StkID)} + case oldtrace.EvGoBlockSync: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSync]), uint64(ev.StkID)} + case oldtrace.EvGoBlockCond: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSyncCond]), uint64(ev.StkID)} + case oldtrace.EvGoBlockNet: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sNetwork]), uint64(ev.StkID)} + case oldtrace.EvGoBlockGC: + mappedType = go122.EvGoBlock + mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sMarkAssistWait]), uint64(ev.StkID)} + case oldtrace.EvGoSysCall: + // Look for the next event for the same G to determine if the syscall + // blocked. + blocked := false + it.events.All()(func(nev *oldtrace.Event) bool { + if nev.G != ev.G { + return true + } + // After an EvGoSysCall, the next event on the same G will either be + // EvGoSysBlock to denote a blocking syscall, or some other event + // (or the end of the trace) if the syscall didn't block. + if nev.Type == oldtrace.EvGoSysBlock { + blocked = true + } + return false + }) + if blocked { + mappedType = go122.EvGoSyscallBegin + mappedArgs = timedEventArgs{1: uint64(ev.StkID)} + } else { + // Convert the old instantaneous syscall event to a pair of syscall + // begin and syscall end and give it the shortest possible duration, + // 1ns. + out1 := Event{ + ctx: schedCtx{ + G: GoID(ev.G), + P: ProcID(ev.P), + M: it.procMs[ProcID(ev.P)], + }, + table: it.evt, + base: baseEvent{ + typ: go122.EvGoSyscallBegin, + time: Time(ev.Ts), + args: timedEventArgs{1: uint64(ev.StkID)}, + }, + } + + out2 := Event{ + ctx: out1.ctx, + table: it.evt, + base: baseEvent{ + typ: go122.EvGoSyscallEnd, + time: Time(ev.Ts + 1), + args: timedEventArgs{}, + }, + } + + it.extra = append(it.extra, out2) + return out1, nil + } + + case oldtrace.EvGoSysExit: + mappedType = go122.EvGoSyscallEndBlocked + case oldtrace.EvGoSysBlock: + return Event{}, errSkip + case oldtrace.EvGoWaiting: + mappedType = go122.EvGoStatus + mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoWaiting)} + delete(it.createdPreInit, GoID(ev.Args[0])) + case oldtrace.EvGoInSyscall: + mappedType = go122.EvGoStatus + // In the new tracer, GoStatus with GoSyscall knows what thread the + // syscall is on. In the old tracer, EvGoInSyscall doesn't contain that + // information and all we can do here is specify NoThread. + mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoSyscall)} + delete(it.createdPreInit, GoID(ev.Args[0])) + case oldtrace.EvHeapAlloc: + mappedType = go122.EvHeapAlloc + case oldtrace.EvHeapGoal: + mappedType = go122.EvHeapGoal + case oldtrace.EvGCMarkAssistStart: + mappedType = go122.EvGCMarkAssistBegin + case oldtrace.EvGCMarkAssistDone: + mappedType = go122.EvGCMarkAssistEnd + case oldtrace.EvUserTaskCreate: + mappedType = go122.EvUserTaskBegin + parent := ev.Args[1] + if parent == 0 { + parent = uint64(NoTask) + } + mappedArgs = timedEventArgs{ev.Args[0], parent, ev.Args[2], uint64(ev.StkID)} + name, _ := it.evt.strings.get(stringID(ev.Args[2])) + it.tasks[TaskID(ev.Args[0])] = taskState{name: name, parentID: TaskID(ev.Args[1])} + case oldtrace.EvUserTaskEnd: + mappedType = go122.EvUserTaskEnd + // Event.Task expects the parent and name to be smuggled in extra args + // and as extra strings. + ts, ok := it.tasks[TaskID(ev.Args[0])] + if ok { + delete(it.tasks, TaskID(ev.Args[0])) + mappedArgs = timedEventArgs{ + ev.Args[0], + ev.Args[1], + uint64(ts.parentID), + uint64(it.evt.addExtraString(ts.name)), + } + } else { + mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], uint64(NoTask), uint64(it.evt.addExtraString(""))} + } + case oldtrace.EvUserRegion: + switch ev.Args[1] { + case 0: // start + mappedType = go122.EvUserRegionBegin + case 1: // end + mappedType = go122.EvUserRegionEnd + } + mappedArgs = timedEventArgs{ev.Args[0], ev.Args[2], uint64(ev.StkID)} + case oldtrace.EvUserLog: + mappedType = go122.EvUserLog + mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], it.inlineToStringID[ev.Args[3]], uint64(ev.StkID)} + case oldtrace.EvCPUSample: + mappedType = go122.EvCPUSample + // When emitted by the Go 1.22 tracer, CPU samples have 5 arguments: + // timestamp, M, P, G, stack. However, after they get turned into Event, + // they have the arguments stack, M, P, G. + // + // In Go 1.21, CPU samples did not have Ms. + mappedArgs = timedEventArgs{uint64(ev.StkID), ^uint64(0), uint64(ev.P), ev.G} + default: + return Event{}, fmt.Errorf("unexpected event type %v", ev.Type) + } + + if oldtrace.EventDescriptions[ev.Type].Stack { + if stackIDs := go122.Specs()[mappedType].StackIDs; len(stackIDs) > 0 { + mappedArgs[stackIDs[0]-1] = uint64(ev.StkID) + } + } + + m := NoThread + if ev.P != -1 && ev.Type != oldtrace.EvCPUSample { + if t, ok := it.procMs[ProcID(ev.P)]; ok { + m = ThreadID(t) + } + } + if ev.Type == oldtrace.EvProcStop { + delete(it.procMs, ProcID(ev.P)) + } + g := GoID(ev.G) + if g == 0 { + g = NoGoroutine + } + out := Event{ + ctx: schedCtx{ + G: GoID(g), + P: ProcID(ev.P), + M: m, + }, + table: it.evt, + base: baseEvent{ + typ: mappedType, + time: Time(ev.Ts), + args: mappedArgs, + }, + } + return out, nil +} + +// convertOldFormat takes a fully loaded trace in the old trace format and +// returns an iterator over events in the new format. +func convertOldFormat(pr oldtrace.Trace) *oldTraceConverter { + it := &oldTraceConverter{} + it.init(pr) + return it +} diff --git a/src/internal/trace/oldtrace_test.go b/src/internal/trace/oldtrace_test.go new file mode 100644 index 0000000000..f812d5ef84 --- /dev/null +++ b/src/internal/trace/oldtrace_test.go @@ -0,0 +1,89 @@ +// Copyright 2024 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 trace_test + +import ( + "internal/trace" + "internal/trace/testtrace" + "io" + "os" + "path/filepath" + "testing" +) + +func TestOldtrace(t *testing.T) { + traces, err := filepath.Glob("./internal/oldtrace/testdata/*_good") + if err != nil { + t.Fatalf("failed to glob for tests: %s", err) + } + var testedUserRegions bool + for _, p := range traces { + p := p + testName, err := filepath.Rel("./internal/oldtrace/testdata", p) + if err != nil { + t.Fatalf("failed to relativize testdata path: %s", err) + } + t.Run(testName, func(t *testing.T) { + f, err := os.Open(p) + if err != nil { + t.Fatalf("failed to open test %q: %s", p, err) + } + defer f.Close() + + tr, err := trace.NewReader(f) + if err != nil { + t.Fatalf("failed to create reader: %s", err) + } + + v := testtrace.NewValidator() + v.Go121 = true + for { + ev, err := tr.ReadEvent() + if err != nil { + if err == io.EOF { + break + } + t.Fatalf("couldn't read converted event: %s", err) + } + if err := v.Event(ev); err != nil { + t.Fatalf("converted event did not validate; event: \n%s\nerror: %s", ev, err) + } + + if testName == "user_task_region_1_21_good" { + testedUserRegions = true + validRegions := map[string]struct{}{ + "post-existing region": struct{}{}, + "region0": struct{}{}, + "region1": struct{}{}, + } + // Check that we correctly convert user regions. These + // strings were generated by + // runtime/trace.TestUserTaskRegion, which is the basis for + // the user_task_region_* test cases. We only check for the + // Go 1.21 traces because earlier traces used different + // strings. + switch ev.Kind() { + case trace.EventRegionBegin, trace.EventRegionEnd: + if _, ok := validRegions[ev.Region().Type]; !ok { + t.Fatalf("converted event has unexpected region type:\n%s", ev) + } + case trace.EventTaskBegin, trace.EventTaskEnd: + if ev.Task().Type != "task0" { + t.Fatalf("converted event has unexpected task type name:\n%s", ev) + } + case trace.EventLog: + l := ev.Log() + if l.Task != 1 || l.Category != "key0" || l.Message != "0123456789abcdef" { + t.Fatalf("converted event has unexpected user log:\n%s", ev) + } + } + } + } + }) + } + if !testedUserRegions { + t.Fatal("didn't see expected test case user_task_region_1_21_good") + } +} diff --git a/src/internal/trace/order.go b/src/internal/trace/order.go new file mode 100644 index 0000000000..9765df6177 --- /dev/null +++ b/src/internal/trace/order.go @@ -0,0 +1,1399 @@ +// 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. + +package trace + +import ( + "fmt" + "strings" + + "internal/trace/event" + "internal/trace/event/go122" + "internal/trace/version" +) + +// ordering emulates Go scheduler state for both validation and +// for putting events in the right order. +// +// The interface to ordering consists of two methods: Advance +// and Next. Advance is called to try and advance an event and +// add completed events to the ordering. Next is used to pick +// off events in the ordering. +type ordering struct { + gStates map[GoID]*gState + pStates map[ProcID]*pState // TODO: The keys are dense, so this can be a slice. + mStates map[ThreadID]*mState + activeTasks map[TaskID]taskState + gcSeq uint64 + gcState gcState + initialGen uint64 + queue queue[Event] +} + +// Advance checks if it's valid to proceed with ev which came from thread m. +// +// It assumes the gen value passed to it is monotonically increasing across calls. +// +// If any error is returned, then the trace is broken and trace parsing must cease. +// If it's not valid to advance with ev, but no error was encountered, the caller +// should attempt to advance with other candidate events from other threads. If the +// caller runs out of candidates, the trace is invalid. +// +// If this returns true, Next is guaranteed to return a complete event. However, +// multiple events may be added to the ordering, so the caller should (but is not +// required to) continue to call Next until it is exhausted. +func (o *ordering) Advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) (bool, error) { + if o.initialGen == 0 { + // Set the initial gen if necessary. + o.initialGen = gen + } + + var curCtx, newCtx schedCtx + curCtx.M = m + newCtx.M = m + + var ms *mState + if m == NoThread { + curCtx.P = NoProc + curCtx.G = NoGoroutine + newCtx = curCtx + } else { + // Pull out or create the mState for this event. + var ok bool + ms, ok = o.mStates[m] + if !ok { + ms = &mState{ + g: NoGoroutine, + p: NoProc, + } + o.mStates[m] = ms + } + curCtx.P = ms.p + curCtx.G = ms.g + newCtx = curCtx + } + + f := orderingDispatch[ev.typ] + if f == nil { + return false, fmt.Errorf("bad event type found while ordering: %v", ev.typ) + } + newCtx, ok, err := f(o, ev, evt, m, gen, curCtx) + if err == nil && ok && ms != nil { + // Update the mState for this event. + ms.p = newCtx.P + ms.g = newCtx.G + } + return ok, err +} + +type orderingHandleFunc func(o *ordering, ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) + +var orderingDispatch = [256]orderingHandleFunc{ + // Procs. + go122.EvProcsChange: (*ordering).advanceAnnotation, + go122.EvProcStart: (*ordering).advanceProcStart, + go122.EvProcStop: (*ordering).advanceProcStop, + go122.EvProcSteal: (*ordering).advanceProcSteal, + go122.EvProcStatus: (*ordering).advanceProcStatus, + + // Goroutines. + go122.EvGoCreate: (*ordering).advanceGoCreate, + go122.EvGoCreateSyscall: (*ordering).advanceGoCreateSyscall, + go122.EvGoStart: (*ordering).advanceGoStart, + go122.EvGoDestroy: (*ordering).advanceGoStopExec, + go122.EvGoDestroySyscall: (*ordering).advanceGoDestroySyscall, + go122.EvGoStop: (*ordering).advanceGoStopExec, + go122.EvGoBlock: (*ordering).advanceGoStopExec, + go122.EvGoUnblock: (*ordering).advanceGoUnblock, + go122.EvGoSyscallBegin: (*ordering).advanceGoSyscallBegin, + go122.EvGoSyscallEnd: (*ordering).advanceGoSyscallEnd, + go122.EvGoSyscallEndBlocked: (*ordering).advanceGoSyscallEndBlocked, + go122.EvGoStatus: (*ordering).advanceGoStatus, + + // STW. + go122.EvSTWBegin: (*ordering).advanceGoRangeBegin, + go122.EvSTWEnd: (*ordering).advanceGoRangeEnd, + + // GC events. + go122.EvGCActive: (*ordering).advanceGCActive, + go122.EvGCBegin: (*ordering).advanceGCBegin, + go122.EvGCEnd: (*ordering).advanceGCEnd, + go122.EvGCSweepActive: (*ordering).advanceGCSweepActive, + go122.EvGCSweepBegin: (*ordering).advanceGCSweepBegin, + go122.EvGCSweepEnd: (*ordering).advanceGCSweepEnd, + go122.EvGCMarkAssistActive: (*ordering).advanceGoRangeActive, + go122.EvGCMarkAssistBegin: (*ordering).advanceGoRangeBegin, + go122.EvGCMarkAssistEnd: (*ordering).advanceGoRangeEnd, + go122.EvHeapAlloc: (*ordering).advanceHeapMetric, + go122.EvHeapGoal: (*ordering).advanceHeapMetric, + + // Annotations. + go122.EvGoLabel: (*ordering).advanceAnnotation, + go122.EvUserTaskBegin: (*ordering).advanceUserTaskBegin, + go122.EvUserTaskEnd: (*ordering).advanceUserTaskEnd, + go122.EvUserRegionBegin: (*ordering).advanceUserRegionBegin, + go122.EvUserRegionEnd: (*ordering).advanceUserRegionEnd, + go122.EvUserLog: (*ordering).advanceAnnotation, + + // Coroutines. Added in Go 1.23. + go122.EvGoSwitch: (*ordering).advanceGoSwitch, + go122.EvGoSwitchDestroy: (*ordering).advanceGoSwitch, + go122.EvGoCreateBlocked: (*ordering).advanceGoCreate, + + // GoStatus event with a stack. Added in Go 1.23. + go122.EvGoStatusStack: (*ordering).advanceGoStatus, + + // Experimental events. + + // Experimental heap span events. Added in Go 1.23. + go122.EvSpan: (*ordering).advanceAllocFree, + go122.EvSpanAlloc: (*ordering).advanceAllocFree, + go122.EvSpanFree: (*ordering).advanceAllocFree, + + // Experimental heap object events. Added in Go 1.23. + go122.EvHeapObject: (*ordering).advanceAllocFree, + go122.EvHeapObjectAlloc: (*ordering).advanceAllocFree, + go122.EvHeapObjectFree: (*ordering).advanceAllocFree, + + // Experimental goroutine stack events. Added in Go 1.23. + go122.EvGoroutineStack: (*ordering).advanceAllocFree, + go122.EvGoroutineStackAlloc: (*ordering).advanceAllocFree, + go122.EvGoroutineStackFree: (*ordering).advanceAllocFree, +} + +func (o *ordering) advanceProcStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + pid := ProcID(ev.args[0]) + status := go122.ProcStatus(ev.args[1]) + if int(status) >= len(go122ProcStatus2ProcState) { + return curCtx, false, fmt.Errorf("invalid status for proc %d: %d", pid, status) + } + oldState := go122ProcStatus2ProcState[status] + if s, ok := o.pStates[pid]; ok { + if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscall { + // ProcSyscallAbandoned is a special case of ProcSyscall. It indicates a + // potential loss of information, but if we're already in ProcSyscall, + // we haven't lost the relevant information. Promote the status and advance. + oldState = ProcRunning + ev.args[1] = uint64(go122.ProcSyscall) + } else if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscallAbandoned { + // If we're passing through ProcSyscallAbandoned, then there's no promotion + // to do. We've lost the M that this P is associated with. However it got there, + // it's going to appear as idle in the API, so pass through as idle. + oldState = ProcIdle + ev.args[1] = uint64(go122.ProcSyscallAbandoned) + } else if s.status != status { + return curCtx, false, fmt.Errorf("inconsistent status for proc %d: old %v vs. new %v", pid, s.status, status) + } + s.seq = makeSeq(gen, 0) // Reset seq. + } else { + o.pStates[pid] = &pState{id: pid, status: status, seq: makeSeq(gen, 0)} + if gen == o.initialGen { + oldState = ProcUndetermined + } else { + oldState = ProcNotExist + } + } + ev.extra(version.Go122)[0] = uint64(oldState) // Smuggle in the old state for StateTransition. + + // Bind the proc to the new context, if it's running. + newCtx := curCtx + if status == go122.ProcRunning || status == go122.ProcSyscall { + newCtx.P = pid + } + // If we're advancing through ProcSyscallAbandoned *but* oldState is running then we've + // promoted it to ProcSyscall. However, because it's ProcSyscallAbandoned, we know this + // P is about to get stolen and its status very likely isn't being emitted by the same + // thread it was bound to. Since this status is Running -> Running and Running is binding, + // we need to make sure we emit it in the right context: the context to which it is bound. + // Find it, and set our current context to it. + if status == go122.ProcSyscallAbandoned && oldState == ProcRunning { + // N.B. This is slow but it should be fairly rare. + found := false + for mid, ms := range o.mStates { + if ms.p == pid { + curCtx.M = mid + curCtx.P = pid + curCtx.G = ms.g + found = true + } + } + if !found { + return curCtx, false, fmt.Errorf("failed to find sched context for proc %d that's about to be stolen", pid) + } + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceProcStart(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + pid := ProcID(ev.args[0]) + seq := makeSeq(gen, ev.args[1]) + + // Try to advance. We might fail here due to sequencing, because the P hasn't + // had a status emitted, or because we already have a P and we're in a syscall, + // and we haven't observed that it was stolen from us yet. + state, ok := o.pStates[pid] + if !ok || state.status != go122.ProcIdle || !seq.succeeds(state.seq) || curCtx.P != NoProc { + // We can't make an inference as to whether this is bad. We could just be seeing + // a ProcStart on a different M before the proc's state was emitted, or before we + // got to the right point in the trace. + // + // Note that we also don't advance here if we have a P and we're in a syscall. + return curCtx, false, nil + } + // We can advance this P. Check some invariants. + // + // We might have a goroutine if a goroutine is exiting a syscall. + reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustNotHave, Goroutine: event.MayHave} + if err := validateCtx(curCtx, reqs); err != nil { + return curCtx, false, err + } + state.status = go122.ProcRunning + state.seq = seq + newCtx := curCtx + newCtx.P = pid + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceProcStop(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // We must be able to advance this P. + // + // There are 2 ways a P can stop: ProcStop and ProcSteal. ProcStop is used when the P + // is stopped by the same M that started it, while ProcSteal is used when another M + // steals the P by stopping it from a distance. + // + // Since a P is bound to an M, and we're stopping on the same M we started, it must + // always be possible to advance the current M's P from a ProcStop. This is also why + // ProcStop doesn't need a sequence number. + state, ok := o.pStates[curCtx.P] + if !ok { + return curCtx, false, fmt.Errorf("event %s for proc (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.P) + } + if state.status != go122.ProcRunning && state.status != go122.ProcSyscall { + return curCtx, false, fmt.Errorf("%s event for proc that's not %s or %s", go122.EventString(ev.typ), go122.ProcRunning, go122.ProcSyscall) + } + reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave} + if err := validateCtx(curCtx, reqs); err != nil { + return curCtx, false, err + } + state.status = go122.ProcIdle + newCtx := curCtx + newCtx.P = NoProc + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceProcSteal(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + pid := ProcID(ev.args[0]) + seq := makeSeq(gen, ev.args[1]) + state, ok := o.pStates[pid] + if !ok || (state.status != go122.ProcSyscall && state.status != go122.ProcSyscallAbandoned) || !seq.succeeds(state.seq) { + // We can't make an inference as to whether this is bad. We could just be seeing + // a ProcStart on a different M before the proc's state was emitted, or before we + // got to the right point in the trace. + return curCtx, false, nil + } + // We can advance this P. Check some invariants. + reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave} + if err := validateCtx(curCtx, reqs); err != nil { + return curCtx, false, err + } + // Smuggle in the P state that let us advance so we can surface information to the event. + // Specifically, we need to make sure that the event is interpreted not as a transition of + // ProcRunning -> ProcIdle but ProcIdle -> ProcIdle instead. + // + // ProcRunning is binding, but we may be running with a P on the current M and we can't + // bind another P. This P is about to go ProcIdle anyway. + oldStatus := state.status + ev.extra(version.Go122)[0] = uint64(oldStatus) + + // Update the P's status and sequence number. + state.status = go122.ProcIdle + state.seq = seq + + // If we've lost information then don't try to do anything with the M. + // It may have moved on and we can't be sure. + if oldStatus == go122.ProcSyscallAbandoned { + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil + } + + // Validate that the M we're stealing from is what we expect. + mid := ThreadID(ev.args[2]) // The M we're stealing from. + + newCtx := curCtx + if mid == curCtx.M { + // We're stealing from ourselves. This behaves like a ProcStop. + if curCtx.P != pid { + return curCtx, false, fmt.Errorf("tried to self-steal proc %d (thread %d), but got proc %d instead", pid, mid, curCtx.P) + } + newCtx.P = NoProc + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil + } + + // We're stealing from some other M. + mState, ok := o.mStates[mid] + if !ok { + return curCtx, false, fmt.Errorf("stole proc from non-existent thread %d", mid) + } + + // Make sure we're actually stealing the right P. + if mState.p != pid { + return curCtx, false, fmt.Errorf("tried to steal proc %d from thread %d, but got proc %d instead", pid, mid, mState.p) + } + + // Tell the M it has no P so it can proceed. + // + // This is safe because we know the P was in a syscall and + // the other M must be trying to get out of the syscall. + // GoSyscallEndBlocked cannot advance until the corresponding + // M loses its P. + mState.p = NoProc + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceGoStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + gid := GoID(ev.args[0]) + mid := ThreadID(ev.args[1]) + status := go122.GoStatus(ev.args[2]) + + if int(status) >= len(go122GoStatus2GoState) { + return curCtx, false, fmt.Errorf("invalid status for goroutine %d: %d", gid, status) + } + oldState := go122GoStatus2GoState[status] + if s, ok := o.gStates[gid]; ok { + if s.status != status { + return curCtx, false, fmt.Errorf("inconsistent status for goroutine %d: old %v vs. new %v", gid, s.status, status) + } + s.seq = makeSeq(gen, 0) // Reset seq. + } else if gen == o.initialGen { + // Set the state. + o.gStates[gid] = &gState{id: gid, status: status, seq: makeSeq(gen, 0)} + oldState = GoUndetermined + } else { + return curCtx, false, fmt.Errorf("found goroutine status for new goroutine after the first generation: id=%v status=%v", gid, status) + } + ev.extra(version.Go122)[0] = uint64(oldState) // Smuggle in the old state for StateTransition. + + newCtx := curCtx + switch status { + case go122.GoRunning: + // Bind the goroutine to the new context, since it's running. + newCtx.G = gid + case go122.GoSyscall: + if mid == NoThread { + return curCtx, false, fmt.Errorf("found goroutine %d in syscall without a thread", gid) + } + // Is the syscall on this thread? If so, bind it to the context. + // Otherwise, we're talking about a G sitting in a syscall on an M. + // Validate the named M. + if mid == curCtx.M { + if gen != o.initialGen && curCtx.G != gid { + // If this isn't the first generation, we *must* have seen this + // binding occur already. Even if the G was blocked in a syscall + // for multiple generations since trace start, we would have seen + // a previous GoStatus event that bound the goroutine to an M. + return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, curCtx.G) + } + newCtx.G = gid + break + } + // Now we're talking about a thread and goroutine that have been + // blocked on a syscall for the entire generation. This case must + // not have a P; the runtime makes sure that all Ps are traced at + // the beginning of a generation, which involves taking a P back + // from every thread. + ms, ok := o.mStates[mid] + if ok { + // This M has been seen. That means we must have seen this + // goroutine go into a syscall on this thread at some point. + if ms.g != gid { + // But the G on the M doesn't match. Something's wrong. + return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, ms.g) + } + // This case is just a Syscall->Syscall event, which needs to + // appear as having the G currently bound to this M. + curCtx.G = ms.g + } else if !ok { + // The M hasn't been seen yet. That means this goroutine + // has just been sitting in a syscall on this M. Create + // a state for it. + o.mStates[mid] = &mState{g: gid, p: NoProc} + // Don't set curCtx.G in this case because this event is the + // binding event (and curCtx represents the "before" state). + } + // Update the current context to the M we're talking about. + curCtx.M = mid + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceGoCreate(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Goroutines must be created on a running P, but may or may not be created + // by a running goroutine. + reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave} + if err := validateCtx(curCtx, reqs); err != nil { + return curCtx, false, err + } + // If we have a goroutine, it must be running. + if state, ok := o.gStates[curCtx.G]; ok && state.status != go122.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + } + // This goroutine created another. Add a state for it. + newgid := GoID(ev.args[0]) + if _, ok := o.gStates[newgid]; ok { + return curCtx, false, fmt.Errorf("tried to create goroutine (%v) that already exists", newgid) + } + status := go122.GoRunnable + if ev.typ == go122.EvGoCreateBlocked { + status = go122.GoWaiting + } + o.gStates[newgid] = &gState{id: newgid, status: status, seq: makeSeq(gen, 0)} + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGoStopExec(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // These are goroutine events that all require an active running + // goroutine on some thread. They must *always* be advance-able, + // since running goroutines are bound to their M. + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + state, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + } + if state.status != go122.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + } + // Handle each case slightly differently; we just group them together + // because they have shared preconditions. + newCtx := curCtx + switch ev.typ { + case go122.EvGoDestroy: + // This goroutine is exiting itself. + delete(o.gStates, curCtx.G) + newCtx.G = NoGoroutine + case go122.EvGoStop: + // Goroutine stopped (yielded). It's runnable but not running on this M. + state.status = go122.GoRunnable + newCtx.G = NoGoroutine + case go122.EvGoBlock: + // Goroutine blocked. It's waiting now and not running on this M. + state.status = go122.GoWaiting + newCtx.G = NoGoroutine + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceGoStart(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + gid := GoID(ev.args[0]) + seq := makeSeq(gen, ev.args[1]) + state, ok := o.gStates[gid] + if !ok || state.status != go122.GoRunnable || !seq.succeeds(state.seq) { + // We can't make an inference as to whether this is bad. We could just be seeing + // a GoStart on a different M before the goroutine was created, before it had its + // state emitted, or before we got to the right point in the trace yet. + return curCtx, false, nil + } + // We can advance this goroutine. Check some invariants. + reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MustNotHave} + if err := validateCtx(curCtx, reqs); err != nil { + return curCtx, false, err + } + state.status = go122.GoRunning + state.seq = seq + newCtx := curCtx + newCtx.G = gid + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceGoUnblock(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // N.B. These both reference the goroutine to unblock, not the current goroutine. + gid := GoID(ev.args[0]) + seq := makeSeq(gen, ev.args[1]) + state, ok := o.gStates[gid] + if !ok || state.status != go122.GoWaiting || !seq.succeeds(state.seq) { + // We can't make an inference as to whether this is bad. We could just be seeing + // a GoUnblock on a different M before the goroutine was created and blocked itself, + // before it had its state emitted, or before we got to the right point in the trace yet. + return curCtx, false, nil + } + state.status = go122.GoRunnable + state.seq = seq + // N.B. No context to validate. Basically anything can unblock + // a goroutine (e.g. sysmon). + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGoSwitch(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // GoSwitch and GoSwitchDestroy represent a trio of events: + // - Unblock of the goroutine to switch to. + // - Block or destroy of the current goroutine. + // - Start executing the next goroutine. + // + // Because it acts like a GoStart for the next goroutine, we can + // only advance it if the sequence numbers line up. + // + // The current goroutine on the thread must be actively running. + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + curGState, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + } + if curGState.status != go122.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + } + nextg := GoID(ev.args[0]) + seq := makeSeq(gen, ev.args[1]) // seq is for nextg, not curCtx.G. + nextGState, ok := o.gStates[nextg] + if !ok || nextGState.status != go122.GoWaiting || !seq.succeeds(nextGState.seq) { + // We can't make an inference as to whether this is bad. We could just be seeing + // a GoSwitch on a different M before the goroutine was created, before it had its + // state emitted, or before we got to the right point in the trace yet. + return curCtx, false, nil + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + + // Update the state of the executing goroutine and emit an event for it + // (GoSwitch and GoSwitchDestroy will be interpreted as GoUnblock events + // for nextg). + switch ev.typ { + case go122.EvGoSwitch: + // Goroutine blocked. It's waiting now and not running on this M. + curGState.status = go122.GoWaiting + + // Emit a GoBlock event. + // TODO(mknyszek): Emit a reason. + o.queue.push(makeEvent(evt, curCtx, go122.EvGoBlock, ev.time, 0 /* no reason */, 0 /* no stack */)) + case go122.EvGoSwitchDestroy: + // This goroutine is exiting itself. + delete(o.gStates, curCtx.G) + + // Emit a GoDestroy event. + o.queue.push(makeEvent(evt, curCtx, go122.EvGoDestroy, ev.time)) + } + // Update the state of the next goroutine. + nextGState.status = go122.GoRunning + nextGState.seq = seq + newCtx := curCtx + newCtx.G = nextg + + // Queue an event for the next goroutine starting to run. + startCtx := curCtx + startCtx.G = NoGoroutine + o.queue.push(makeEvent(evt, startCtx, go122.EvGoStart, ev.time, uint64(nextg), ev.args[1])) + return newCtx, true, nil +} + +func (o *ordering) advanceGoSyscallBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Entering a syscall requires an active running goroutine with a + // proc on some thread. It is always advancable. + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + state, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + } + if state.status != go122.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + } + // Goroutine entered a syscall. It's still running on this P and M. + state.status = go122.GoSyscall + pState, ok := o.pStates[curCtx.P] + if !ok { + return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) + } + pState.status = go122.ProcSyscall + // Validate the P sequence number on the event and advance it. + // + // We have a P sequence number for what is supposed to be a goroutine event + // so that we can correctly model P stealing. Without this sequence number here, + // the syscall from which a ProcSteal event is stealing can be ambiguous in the + // face of broken timestamps. See the go122-syscall-steal-proc-ambiguous test for + // more details. + // + // Note that because this sequence number only exists as a tool for disambiguation, + // we can enforce that we have the right sequence number at this point; we don't need + // to back off and see if any other events will advance. This is a running P. + pSeq := makeSeq(gen, ev.args[0]) + if !pSeq.succeeds(pState.seq) { + return curCtx, false, fmt.Errorf("failed to advance %s: can't make sequence: %s -> %s", go122.EventString(ev.typ), pState.seq, pSeq) + } + pState.seq = pSeq + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGoSyscallEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // This event is always advance-able because it happens on the same + // thread that EvGoSyscallStart happened, and the goroutine can't leave + // that thread until its done. + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + state, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + } + if state.status != go122.GoSyscall { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + } + state.status = go122.GoRunning + + // Transfer the P back to running from syscall. + pState, ok := o.pStates[curCtx.P] + if !ok { + return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) + } + if pState.status != go122.ProcSyscall { + return curCtx, false, fmt.Errorf("expected proc %d in state %v, but got %v instead", curCtx.P, go122.ProcSyscall, pState.status) + } + pState.status = go122.ProcRunning + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGoSyscallEndBlocked(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // This event becomes advanceable when its P is not in a syscall state + // (lack of a P altogether is also acceptable for advancing). + // The transfer out of ProcSyscall can happen either voluntarily via + // ProcStop or involuntarily via ProcSteal. We may also acquire a new P + // before we get here (after the transfer out) but that's OK: that new + // P won't be in the ProcSyscall state anymore. + // + // Basically: while we have a preemptible P, don't advance, because we + // *know* from the event that we're going to lose it at some point during + // the syscall. We shouldn't advance until that happens. + if curCtx.P != NoProc { + pState, ok := o.pStates[curCtx.P] + if !ok { + return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) + } + if pState.status == go122.ProcSyscall { + return curCtx, false, nil + } + } + // As mentioned above, we may have a P here if we ProcStart + // before this event. + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil { + return curCtx, false, err + } + state, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + } + if state.status != go122.GoSyscall { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + } + newCtx := curCtx + newCtx.G = NoGoroutine + state.status = go122.GoRunnable + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceGoCreateSyscall(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // This event indicates that a goroutine is effectively + // being created out of a cgo callback. Such a goroutine + // is 'created' in the syscall state. + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustNotHave}); err != nil { + return curCtx, false, err + } + // This goroutine is effectively being created. Add a state for it. + newgid := GoID(ev.args[0]) + if _, ok := o.gStates[newgid]; ok { + return curCtx, false, fmt.Errorf("tried to create goroutine (%v) in syscall that already exists", newgid) + } + o.gStates[newgid] = &gState{id: newgid, status: go122.GoSyscall, seq: makeSeq(gen, 0)} + // Goroutine is executing. Bind it to the context. + newCtx := curCtx + newCtx.G = newgid + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceGoDestroySyscall(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // This event indicates that a goroutine created for a + // cgo callback is disappearing, either because the callback + // ending or the C thread that called it is being destroyed. + // + // Also, treat this as if we lost our P too. + // The thread ID may be reused by the platform and we'll get + // really confused if we try to steal the P is this is running + // with later. The new M with the same ID could even try to + // steal back this P from itself! + // + // The runtime is careful to make sure that any GoCreateSyscall + // event will enter the runtime emitting events for reacquiring a P. + // + // Note: we might have a P here. The P might not be released + // eagerly by the runtime, and it might get stolen back later + // (or never again, if the program is going to exit). + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil { + return curCtx, false, err + } + // Check to make sure the goroutine exists in the right state. + state, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + } + if state.status != go122.GoSyscall { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %v", go122.EventString(ev.typ), GoSyscall) + } + // This goroutine is exiting itself. + delete(o.gStates, curCtx.G) + newCtx := curCtx + newCtx.G = NoGoroutine + + // If we have a proc, then we're dissociating from it now. See the comment at the top of the case. + if curCtx.P != NoProc { + pState, ok := o.pStates[curCtx.P] + if !ok { + return curCtx, false, fmt.Errorf("found invalid proc %d during %s", curCtx.P, go122.EventString(ev.typ)) + } + if pState.status != go122.ProcSyscall { + return curCtx, false, fmt.Errorf("proc %d in unexpected state %s during %s", curCtx.P, pState.status, go122.EventString(ev.typ)) + } + // See the go122-create-syscall-reuse-thread-id test case for more details. + pState.status = go122.ProcSyscallAbandoned + newCtx.P = NoProc + + // Queue an extra self-ProcSteal event. + extra := makeEvent(evt, curCtx, go122.EvProcSteal, ev.time, uint64(curCtx.P)) + extra.base.extra(version.Go122)[0] = uint64(go122.ProcSyscall) + o.queue.push(extra) + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return newCtx, true, nil +} + +func (o *ordering) advanceUserTaskBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Handle tasks. Tasks are interesting because: + // - There's no Begin event required to reference a task. + // - End for a particular task ID can appear multiple times. + // As a result, there's very little to validate. The only + // thing we have to be sure of is that a task didn't begin + // after it had already begun. Task IDs are allowed to be + // reused, so we don't care about a Begin after an End. + id := TaskID(ev.args[0]) + if _, ok := o.activeTasks[id]; ok { + return curCtx, false, fmt.Errorf("task ID conflict: %d", id) + } + // Get the parent ID, but don't validate it. There's no guarantee + // we actually have information on whether it's active. + parentID := TaskID(ev.args[1]) + if parentID == BackgroundTask { + // Note: a value of 0 here actually means no parent, *not* the + // background task. Automatic background task attachment only + // applies to regions. + parentID = NoTask + ev.args[1] = uint64(NoTask) + } + + // Validate the name and record it. We'll need to pass it through to + // EvUserTaskEnd. + nameID := stringID(ev.args[2]) + name, ok := evt.strings.get(nameID) + if !ok { + return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ) + } + o.activeTasks[id] = taskState{name: name, parentID: parentID} + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceUserTaskEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + id := TaskID(ev.args[0]) + if ts, ok := o.activeTasks[id]; ok { + // Smuggle the task info. This may happen in a different generation, + // which may not have the name in its string table. Add it to the extra + // strings table so we can look it up later. + ev.extra(version.Go122)[0] = uint64(ts.parentID) + ev.extra(version.Go122)[1] = uint64(evt.addExtraString(ts.name)) + delete(o.activeTasks, id) + } else { + // Explicitly clear the task info. + ev.extra(version.Go122)[0] = uint64(NoTask) + ev.extra(version.Go122)[1] = uint64(evt.addExtraString("")) + } + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceUserRegionBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + tid := TaskID(ev.args[0]) + nameID := stringID(ev.args[1]) + name, ok := evt.strings.get(nameID) + if !ok { + return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ) + } + gState, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("encountered EvUserRegionBegin without known state for current goroutine %d", curCtx.G) + } + if err := gState.beginRegion(userRegion{tid, name}); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceUserRegionEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + tid := TaskID(ev.args[0]) + nameID := stringID(ev.args[1]) + name, ok := evt.strings.get(nameID) + if !ok { + return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ) + } + gState, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("encountered EvUserRegionEnd without known state for current goroutine %d", curCtx.G) + } + if err := gState.endRegion(userRegion{tid, name}); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +// Handle the GC mark phase. +// +// We have sequence numbers for both start and end because they +// can happen on completely different threads. We want an explicit +// partial order edge between start and end here, otherwise we're +// relying entirely on timestamps to make sure we don't advance a +// GCEnd for a _different_ GC cycle if timestamps are wildly broken. +func (o *ordering) advanceGCActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + seq := ev.args[0] + if gen == o.initialGen { + if o.gcState != gcUndetermined { + return curCtx, false, fmt.Errorf("GCActive in the first generation isn't first GC event") + } + o.gcSeq = seq + o.gcState = gcRunning + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil + } + if seq != o.gcSeq+1 { + // This is not the right GC cycle. + return curCtx, false, nil + } + if o.gcState != gcRunning { + return curCtx, false, fmt.Errorf("encountered GCActive while GC was not in progress") + } + o.gcSeq = seq + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGCBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + seq := ev.args[0] + if o.gcState == gcUndetermined { + o.gcSeq = seq + o.gcState = gcRunning + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil + } + if seq != o.gcSeq+1 { + // This is not the right GC cycle. + return curCtx, false, nil + } + if o.gcState == gcRunning { + return curCtx, false, fmt.Errorf("encountered GCBegin while GC was already in progress") + } + o.gcSeq = seq + o.gcState = gcRunning + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGCEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + seq := ev.args[0] + if seq != o.gcSeq+1 { + // This is not the right GC cycle. + return curCtx, false, nil + } + if o.gcState == gcNotRunning { + return curCtx, false, fmt.Errorf("encountered GCEnd when GC was not in progress") + } + if o.gcState == gcUndetermined { + return curCtx, false, fmt.Errorf("encountered GCEnd when GC was in an undetermined state") + } + o.gcSeq = seq + o.gcState = gcNotRunning + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceAnnotation(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Handle simple instantaneous events that require a G. + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceHeapMetric(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Handle allocation metrics, which don't require a G. + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGCSweepBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Handle sweep, which is bound to a P and doesn't require a G. + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { + return curCtx, false, err + } + if err := o.pStates[curCtx.P].beginRange(makeRangeType(ev.typ, 0)); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGCSweepActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + pid := ProcID(ev.args[0]) + // N.B. In practice Ps can't block while they're sweeping, so this can only + // ever reference curCtx.P. However, be lenient about this like we are with + // GCMarkAssistActive; there's no reason the runtime couldn't change to block + // in the middle of a sweep. + pState, ok := o.pStates[pid] + if !ok { + return curCtx, false, fmt.Errorf("encountered GCSweepActive for unknown proc %d", pid) + } + if err := pState.activeRange(makeRangeType(ev.typ, 0), gen == o.initialGen); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGCSweepEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { + return curCtx, false, err + } + _, err := o.pStates[curCtx.P].endRange(ev.typ) + if err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGoRangeBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Handle special goroutine-bound event ranges. + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + desc := stringID(0) + if ev.typ == go122.EvSTWBegin { + desc = stringID(ev.args[0]) + } + gState, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", ev.typ, curCtx.G) + } + if err := gState.beginRange(makeRangeType(ev.typ, desc)); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGoRangeActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + gid := GoID(ev.args[0]) + // N.B. Like GoStatus, this can happen at any time, because it can + // reference a non-running goroutine. Don't check anything about the + // current scheduler context. + gState, ok := o.gStates[gid] + if !ok { + return curCtx, false, fmt.Errorf("uninitialized goroutine %d found during %s", gid, go122.EventString(ev.typ)) + } + if err := gState.activeRange(makeRangeType(ev.typ, 0), gen == o.initialGen); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceGoRangeEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + return curCtx, false, err + } + gState, ok := o.gStates[curCtx.G] + if !ok { + return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", ev.typ, curCtx.G) + } + desc, err := gState.endRange(ev.typ) + if err != nil { + return curCtx, false, err + } + if ev.typ == go122.EvSTWEnd { + // Smuggle the kind into the event. + // Don't use ev.extra here so we have symmetry with STWBegin. + ev.args[0] = uint64(desc) + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +func (o *ordering) advanceAllocFree(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { + // Handle simple instantaneous events that may or may not have a P. + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave}); err != nil { + return curCtx, false, err + } + o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) + return curCtx, true, nil +} + +// Next returns the next event in the ordering. +func (o *ordering) Next() (Event, bool) { + return o.queue.pop() +} + +// schedCtx represents the scheduling resources associated with an event. +type schedCtx struct { + G GoID + P ProcID + M ThreadID +} + +// validateCtx ensures that ctx conforms to some reqs, returning an error if +// it doesn't. +func validateCtx(ctx schedCtx, reqs event.SchedReqs) error { + // Check thread requirements. + if reqs.Thread == event.MustHave && ctx.M == NoThread { + return fmt.Errorf("expected a thread but didn't have one") + } else if reqs.Thread == event.MustNotHave && ctx.M != NoThread { + return fmt.Errorf("expected no thread but had one") + } + + // Check proc requirements. + if reqs.Proc == event.MustHave && ctx.P == NoProc { + return fmt.Errorf("expected a proc but didn't have one") + } else if reqs.Proc == event.MustNotHave && ctx.P != NoProc { + return fmt.Errorf("expected no proc but had one") + } + + // Check goroutine requirements. + if reqs.Goroutine == event.MustHave && ctx.G == NoGoroutine { + return fmt.Errorf("expected a goroutine but didn't have one") + } else if reqs.Goroutine == event.MustNotHave && ctx.G != NoGoroutine { + return fmt.Errorf("expected no goroutine but had one") + } + return nil +} + +// gcState is a trinary variable for the current state of the GC. +// +// The third state besides "enabled" and "disabled" is "undetermined." +type gcState uint8 + +const ( + gcUndetermined gcState = iota + gcNotRunning + gcRunning +) + +// String returns a human-readable string for the GC state. +func (s gcState) String() string { + switch s { + case gcUndetermined: + return "Undetermined" + case gcNotRunning: + return "NotRunning" + case gcRunning: + return "Running" + } + return "Bad" +} + +// userRegion represents a unique user region when attached to some gState. +type userRegion struct { + // name must be a resolved string because the string ID for the same + // string may change across generations, but we care about checking + // the value itself. + taskID TaskID + name string +} + +// rangeType is a way to classify special ranges of time. +// +// These typically correspond 1:1 with "Begin" events, but +// they may have an optional subtype that describes the range +// in more detail. +type rangeType struct { + typ event.Type // "Begin" event. + desc stringID // Optional subtype. +} + +// makeRangeType constructs a new rangeType. +func makeRangeType(typ event.Type, desc stringID) rangeType { + if styp := go122.Specs()[typ].StartEv; styp != go122.EvNone { + typ = styp + } + return rangeType{typ, desc} +} + +// gState is the state of a goroutine at a point in the trace. +type gState struct { + id GoID + status go122.GoStatus + seq seqCounter + + // regions are the active user regions for this goroutine. + regions []userRegion + + // rangeState is the state of special time ranges bound to this goroutine. + rangeState +} + +// beginRegion starts a user region on the goroutine. +func (s *gState) beginRegion(r userRegion) error { + s.regions = append(s.regions, r) + return nil +} + +// endRegion ends a user region on the goroutine. +func (s *gState) endRegion(r userRegion) error { + if len(s.regions) == 0 { + // We do not know about regions that began before tracing started. + return nil + } + if next := s.regions[len(s.regions)-1]; next != r { + return fmt.Errorf("misuse of region in goroutine %v: region end %v when the inner-most active region start event is %v", s.id, r, next) + } + s.regions = s.regions[:len(s.regions)-1] + return nil +} + +// pState is the state of a proc at a point in the trace. +type pState struct { + id ProcID + status go122.ProcStatus + seq seqCounter + + // rangeState is the state of special time ranges bound to this proc. + rangeState +} + +// mState is the state of a thread at a point in the trace. +type mState struct { + g GoID // Goroutine bound to this M. (The goroutine's state is Executing.) + p ProcID // Proc bound to this M. (The proc's state is Executing.) +} + +// rangeState represents the state of special time ranges. +type rangeState struct { + // inFlight contains the rangeTypes of any ranges bound to a resource. + inFlight []rangeType +} + +// beginRange begins a special range in time on the goroutine. +// +// Returns an error if the range is already in progress. +func (s *rangeState) beginRange(typ rangeType) error { + if s.hasRange(typ) { + return fmt.Errorf("discovered event already in-flight for when starting event %v", go122.Specs()[typ.typ].Name) + } + s.inFlight = append(s.inFlight, typ) + return nil +} + +// activeRange marks special range in time on the goroutine as active in the +// initial generation, or confirms that it is indeed active in later generations. +func (s *rangeState) activeRange(typ rangeType, isInitialGen bool) error { + if isInitialGen { + if s.hasRange(typ) { + return fmt.Errorf("found named active range already in first gen: %v", typ) + } + s.inFlight = append(s.inFlight, typ) + } else if !s.hasRange(typ) { + return fmt.Errorf("resource is missing active range: %v %v", go122.Specs()[typ.typ].Name, s.inFlight) + } + return nil +} + +// hasRange returns true if a special time range on the goroutine as in progress. +func (s *rangeState) hasRange(typ rangeType) bool { + for _, ftyp := range s.inFlight { + if ftyp == typ { + return true + } + } + return false +} + +// endsRange ends a special range in time on the goroutine. +// +// This must line up with the start event type of the range the goroutine is currently in. +func (s *rangeState) endRange(typ event.Type) (stringID, error) { + st := go122.Specs()[typ].StartEv + idx := -1 + for i, r := range s.inFlight { + if r.typ == st { + idx = i + break + } + } + if idx < 0 { + return 0, fmt.Errorf("tried to end event %v, but not in-flight", go122.Specs()[st].Name) + } + // Swap remove. + desc := s.inFlight[idx].desc + s.inFlight[idx], s.inFlight[len(s.inFlight)-1] = s.inFlight[len(s.inFlight)-1], s.inFlight[idx] + s.inFlight = s.inFlight[:len(s.inFlight)-1] + return desc, nil +} + +// seqCounter represents a global sequence counter for a resource. +type seqCounter struct { + gen uint64 // The generation for the local sequence counter seq. + seq uint64 // The sequence number local to the generation. +} + +// makeSeq creates a new seqCounter. +func makeSeq(gen, seq uint64) seqCounter { + return seqCounter{gen: gen, seq: seq} +} + +// succeeds returns true if a is the immediate successor of b. +func (a seqCounter) succeeds(b seqCounter) bool { + return a.gen == b.gen && a.seq == b.seq+1 +} + +// String returns a debug string representation of the seqCounter. +func (c seqCounter) String() string { + return fmt.Sprintf("%d (gen=%d)", c.seq, c.gen) +} + +func dumpOrdering(order *ordering) string { + var sb strings.Builder + for id, state := range order.gStates { + fmt.Fprintf(&sb, "G %d [status=%s seq=%s]\n", id, state.status, state.seq) + } + fmt.Fprintln(&sb) + for id, state := range order.pStates { + fmt.Fprintf(&sb, "P %d [status=%s seq=%s]\n", id, state.status, state.seq) + } + fmt.Fprintln(&sb) + for id, state := range order.mStates { + fmt.Fprintf(&sb, "M %d [g=%d p=%d]\n", id, state.g, state.p) + } + fmt.Fprintln(&sb) + fmt.Fprintf(&sb, "GC %d %s\n", order.gcSeq, order.gcState) + return sb.String() +} + +// taskState represents an active task. +type taskState struct { + // name is the type of the active task. + name string + + // parentID is the parent ID of the active task. + parentID TaskID +} + +// queue implements a growable ring buffer with a queue API. +type queue[T any] struct { + start, end int + buf []T +} + +// push adds a new event to the back of the queue. +func (q *queue[T]) push(value T) { + if q.end-q.start == len(q.buf) { + q.grow() + } + q.buf[q.end%len(q.buf)] = value + q.end++ +} + +// grow increases the size of the queue. +func (q *queue[T]) grow() { + if len(q.buf) == 0 { + q.buf = make([]T, 2) + return + } + + // Create new buf and copy data over. + newBuf := make([]T, len(q.buf)*2) + pivot := q.start % len(q.buf) + first, last := q.buf[pivot:], q.buf[:pivot] + copy(newBuf[:len(first)], first) + copy(newBuf[len(first):], last) + + // Update the queue state. + q.start = 0 + q.end = len(q.buf) + q.buf = newBuf +} + +// pop removes an event from the front of the queue. If the +// queue is empty, it returns an EventBad event. +func (q *queue[T]) pop() (T, bool) { + if q.end-q.start == 0 { + return *new(T), false + } + elem := &q.buf[q.start%len(q.buf)] + value := *elem + *elem = *new(T) // Clear the entry before returning, so we don't hold onto old tables. + q.start++ + return value, true +} + +// makeEvent creates an Event from the provided information. +// +// It's just a convenience function; it's always OK to construct +// an Event manually if this isn't quite the right way to express +// the contents of the event. +func makeEvent(table *evTable, ctx schedCtx, typ event.Type, time Time, args ...uint64) Event { + ev := Event{ + table: table, + ctx: ctx, + base: baseEvent{ + typ: typ, + time: time, + }, + } + copy(ev.base.args[:], args) + return ev +} diff --git a/src/internal/trace/order_test.go b/src/internal/trace/order_test.go new file mode 100644 index 0000000000..e267512a53 --- /dev/null +++ b/src/internal/trace/order_test.go @@ -0,0 +1,34 @@ +// 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. + +package trace + +import "testing" + +func TestQueue(t *testing.T) { + var q queue[int] + check := func(name string, exp []int) { + for _, v := range exp { + q.push(v) + } + for i, want := range exp { + if got, ok := q.pop(); !ok { + t.Fatalf("check %q: expected to be able to pop after %d pops", name, i+1) + } else if got != want { + t.Fatalf("check %q: expected value %d after on pop %d, got %d", name, want, i+1, got) + } + } + if _, ok := q.pop(); ok { + t.Fatalf("check %q: did not expect to be able to pop more values", name) + } + if _, ok := q.pop(); ok { + t.Fatalf("check %q: did not expect to be able to pop more values a second time", name) + } + } + check("one element", []int{4}) + check("two elements", []int{64, 12}) + check("six elements", []int{55, 16423, 2352, 644, 12874, 9372}) + check("one element again", []int{7}) + check("two elements again", []int{77, 6336}) +} diff --git a/src/internal/trace/raw/doc.go b/src/internal/trace/raw/doc.go new file mode 100644 index 0000000000..53487372b7 --- /dev/null +++ b/src/internal/trace/raw/doc.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. + +/* +Package raw provides an interface to interpret and emit Go execution traces. +It can interpret and emit execution traces in its wire format as well as a +bespoke but simple text format. + +The readers and writers in this package perform no validation on or ordering of +the input, and so are generally unsuitable for analysis. However, they're very +useful for testing and debugging the tracer in the runtime and more sophisticated +trace parsers. + +# Text format specification + +The trace text format produced and consumed by this package is a line-oriented +format. + +The first line in each text trace is the header line. + + Trace Go1.XX + +Following that is a series of event lines. Each event begins with an +event name, followed by zero or more named unsigned integer arguments. +Names are separated from their integer values by an '=' sign. Names can +consist of any UTF-8 character except '='. + +For example: + + EventName arg1=23 arg2=55 arg3=53 + +Any amount of whitespace is allowed to separate each token. Whitespace +is identified via unicode.IsSpace. + +Some events have additional data on following lines. There are two such +special cases. + +The first special case consists of events with trailing byte-oriented data. +The trailer begins on the following line from the event. That line consists +of a single argument 'data' and a Go-quoted string representing the byte data +within. Note: an explicit argument for the length is elided, because it's +just the length of the unquoted string. + +For example: + + String id=5 + data="hello world\x00" + +These events are identified in their spec by the HasData flag. + +The second special case consists of stack events. These events are identified +by the IsStack flag. These events also have a trailing unsigned integer argument +describing the number of stack frame descriptors that follow. Each stack frame +descriptor is on its own line following the event, consisting of four signed +integer arguments: the PC, an integer describing the function name, an integer +describing the file name, and the line number in that file that function was at +at the time the stack trace was taken. + +For example: + + Stack id=5 n=2 + pc=1241251 func=3 file=6 line=124 + pc=7534345 func=6 file=3 line=64 +*/ +package raw diff --git a/src/internal/trace/raw/event.go b/src/internal/trace/raw/event.go new file mode 100644 index 0000000000..4766fbe563 --- /dev/null +++ b/src/internal/trace/raw/event.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. + +package raw + +import ( + "strconv" + "strings" + + "internal/trace/event" + "internal/trace/version" +) + +// Event is a simple representation of a trace event. +// +// Note that this typically includes much more than just +// timestamped events, and it also represents parts of the +// trace format's framing. (But not interpreted.) +type Event struct { + Version version.Version + Ev event.Type + Args []uint64 + Data []byte +} + +// String returns the canonical string representation of the event. +// +// This format is the same format that is parsed by the TextReader +// and emitted by the TextWriter. +func (e *Event) String() string { + spec := e.Version.Specs()[e.Ev] + + var s strings.Builder + s.WriteString(spec.Name) + for i := range spec.Args { + s.WriteString(" ") + s.WriteString(spec.Args[i]) + s.WriteString("=") + s.WriteString(strconv.FormatUint(e.Args[i], 10)) + } + if spec.IsStack { + frames := e.Args[len(spec.Args):] + for i := 0; i < len(frames); i++ { + if i%4 == 0 { + s.WriteString("\n\t") + } else { + s.WriteString(" ") + } + s.WriteString(frameFields[i%4]) + s.WriteString("=") + s.WriteString(strconv.FormatUint(frames[i], 10)) + } + } + if e.Data != nil { + s.WriteString("\n\tdata=") + s.WriteString(strconv.Quote(string(e.Data))) + } + return s.String() +} diff --git a/src/internal/trace/raw/reader.go b/src/internal/trace/raw/reader.go new file mode 100644 index 0000000000..37f36a1a24 --- /dev/null +++ b/src/internal/trace/raw/reader.go @@ -0,0 +1,110 @@ +// 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. + +package raw + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" + + "internal/trace/event" + "internal/trace/version" +) + +// Reader parses trace bytes with only very basic validation +// into an event stream. +type Reader struct { + r *bufio.Reader + v version.Version + specs []event.Spec +} + +// NewReader creates a new reader for the trace wire format. +func NewReader(r io.Reader) (*Reader, error) { + br := bufio.NewReader(r) + v, err := version.ReadHeader(br) + if err != nil { + return nil, err + } + return &Reader{r: br, v: v, specs: v.Specs()}, nil +} + +// Version returns the version of the trace that we're reading. +func (r *Reader) Version() version.Version { + return r.v +} + +// ReadEvent reads and returns the next trace event in the byte stream. +func (r *Reader) ReadEvent() (Event, error) { + evb, err := r.r.ReadByte() + if err == io.EOF { + return Event{}, io.EOF + } + if err != nil { + return Event{}, err + } + if int(evb) >= len(r.specs) || evb == 0 { + return Event{}, fmt.Errorf("invalid event type: %d", evb) + } + ev := event.Type(evb) + spec := r.specs[ev] + args, err := r.readArgs(len(spec.Args)) + if err != nil { + return Event{}, err + } + if spec.IsStack { + len := int(args[1]) + for i := 0; i < len; i++ { + // Each stack frame has four args: pc, func ID, file ID, line number. + frame, err := r.readArgs(4) + if err != nil { + return Event{}, err + } + args = append(args, frame...) + } + } + var data []byte + if spec.HasData { + data, err = r.readData() + if err != nil { + return Event{}, err + } + } + return Event{ + Version: r.v, + Ev: ev, + Args: args, + Data: data, + }, nil +} + +func (r *Reader) readArgs(n int) ([]uint64, error) { + var args []uint64 + for i := 0; i < n; i++ { + val, err := binary.ReadUvarint(r.r) + if err != nil { + return nil, err + } + args = append(args, val) + } + return args, nil +} + +func (r *Reader) readData() ([]byte, error) { + len, err := binary.ReadUvarint(r.r) + if err != nil { + return nil, err + } + var data []byte + for i := 0; i < int(len); i++ { + b, err := r.r.ReadByte() + if err != nil { + return nil, err + } + data = append(data, b) + } + return data, nil +} diff --git a/src/internal/trace/raw/textreader.go b/src/internal/trace/raw/textreader.go new file mode 100644 index 0000000000..37bfcfefdf --- /dev/null +++ b/src/internal/trace/raw/textreader.go @@ -0,0 +1,217 @@ +// 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. + +package raw + +import ( + "bufio" + "fmt" + "io" + "strconv" + "strings" + "unicode" + + "internal/trace/event" + "internal/trace/version" +) + +// TextReader parses a text format trace with only very basic validation +// into an event stream. +type TextReader struct { + v version.Version + specs []event.Spec + names map[string]event.Type + s *bufio.Scanner +} + +// NewTextReader creates a new reader for the trace text format. +func NewTextReader(r io.Reader) (*TextReader, error) { + tr := &TextReader{s: bufio.NewScanner(r)} + line, err := tr.nextLine() + if err != nil { + return nil, err + } + trace, line := readToken(line) + if trace != "Trace" { + return nil, fmt.Errorf("failed to parse header") + } + gover, line := readToken(line) + if !strings.HasPrefix(gover, "Go1.") { + return nil, fmt.Errorf("failed to parse header Go version") + } + rawv, err := strconv.ParseUint(gover[len("Go1."):], 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse header Go version: %v", err) + } + v := version.Version(rawv) + if !v.Valid() { + return nil, fmt.Errorf("unknown or unsupported Go version 1.%d", v) + } + tr.v = v + tr.specs = v.Specs() + tr.names = event.Names(tr.specs) + for _, r := range line { + if !unicode.IsSpace(r) { + return nil, fmt.Errorf("encountered unexpected non-space at the end of the header: %q", line) + } + } + return tr, nil +} + +// Version returns the version of the trace that we're reading. +func (r *TextReader) Version() version.Version { + return r.v +} + +// ReadEvent reads and returns the next trace event in the text stream. +func (r *TextReader) ReadEvent() (Event, error) { + line, err := r.nextLine() + if err != nil { + return Event{}, err + } + evStr, line := readToken(line) + ev, ok := r.names[evStr] + if !ok { + return Event{}, fmt.Errorf("unidentified event: %s", evStr) + } + spec := r.specs[ev] + args, err := readArgs(line, spec.Args) + if err != nil { + return Event{}, fmt.Errorf("reading args for %s: %v", evStr, err) + } + if spec.IsStack { + len := int(args[1]) + for i := 0; i < len; i++ { + line, err := r.nextLine() + if err == io.EOF { + return Event{}, fmt.Errorf("unexpected EOF while reading stack: args=%v", args) + } + if err != nil { + return Event{}, err + } + frame, err := readArgs(line, frameFields) + if err != nil { + return Event{}, err + } + args = append(args, frame...) + } + } + var data []byte + if spec.HasData { + line, err := r.nextLine() + if err == io.EOF { + return Event{}, fmt.Errorf("unexpected EOF while reading data for %s: args=%v", evStr, args) + } + if err != nil { + return Event{}, err + } + data, err = readData(line) + if err != nil { + return Event{}, err + } + } + return Event{ + Version: r.v, + Ev: ev, + Args: args, + Data: data, + }, nil +} + +func (r *TextReader) nextLine() (string, error) { + for { + if !r.s.Scan() { + if err := r.s.Err(); err != nil { + return "", err + } + return "", io.EOF + } + txt := r.s.Text() + tok, _ := readToken(txt) + if tok == "" { + continue // Empty line or comment. + } + return txt, nil + } +} + +var frameFields = []string{"pc", "func", "file", "line"} + +func readArgs(s string, names []string) ([]uint64, error) { + var args []uint64 + for _, name := range names { + arg, value, rest, err := readArg(s) + if err != nil { + return nil, err + } + if arg != name { + return nil, fmt.Errorf("expected argument %q, but got %q", name, arg) + } + args = append(args, value) + s = rest + } + for _, r := range s { + if !unicode.IsSpace(r) { + return nil, fmt.Errorf("encountered unexpected non-space at the end of an event: %q", s) + } + } + return args, nil +} + +func readArg(s string) (arg string, value uint64, rest string, err error) { + var tok string + tok, rest = readToken(s) + if len(tok) == 0 { + return "", 0, s, fmt.Errorf("no argument") + } + parts := strings.SplitN(tok, "=", 2) + if len(parts) < 2 { + return "", 0, s, fmt.Errorf("malformed argument: %q", tok) + } + arg = parts[0] + value, err = strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return arg, value, s, fmt.Errorf("failed to parse argument value %q for arg %q", parts[1], parts[0]) + } + return +} + +func readToken(s string) (token, rest string) { + tkStart := -1 + for i, r := range s { + if r == '#' { + return "", "" + } + if !unicode.IsSpace(r) { + tkStart = i + break + } + } + if tkStart < 0 { + return "", "" + } + tkEnd := -1 + for i, r := range s[tkStart:] { + if unicode.IsSpace(r) || r == '#' { + tkEnd = i + tkStart + break + } + } + if tkEnd < 0 { + return s[tkStart:], "" + } + return s[tkStart:tkEnd], s[tkEnd:] +} + +func readData(line string) ([]byte, error) { + parts := strings.SplitN(line, "=", 2) + if len(parts) < 2 || strings.TrimSpace(parts[0]) != "data" { + return nil, fmt.Errorf("malformed data: %q", line) + } + data, err := strconv.Unquote(strings.TrimSpace(parts[1])) + if err != nil { + return nil, fmt.Errorf("failed to parse data: %q: %v", line, err) + } + return []byte(data), nil +} diff --git a/src/internal/trace/raw/textwriter.go b/src/internal/trace/raw/textwriter.go new file mode 100644 index 0000000000..b9f25923cb --- /dev/null +++ b/src/internal/trace/raw/textwriter.go @@ -0,0 +1,39 @@ +// 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. + +package raw + +import ( + "fmt" + "io" + + "internal/trace/version" +) + +// TextWriter emits the text format of a trace. +type TextWriter struct { + w io.Writer + v version.Version +} + +// NewTextWriter creates a new write for the trace text format. +func NewTextWriter(w io.Writer, v version.Version) (*TextWriter, error) { + _, err := fmt.Fprintf(w, "Trace Go1.%d\n", v) + if err != nil { + return nil, err + } + return &TextWriter{w: w, v: v}, nil +} + +// WriteEvent writes a single event to the stream. +func (w *TextWriter) WriteEvent(e Event) error { + // Check version. + if e.Version != w.v { + return fmt.Errorf("mismatched version between writer (go 1.%d) and event (go 1.%d)", w.v, e.Version) + } + + // Write event. + _, err := fmt.Fprintln(w.w, e.String()) + return err +} diff --git a/src/internal/trace/raw/writer.go b/src/internal/trace/raw/writer.go new file mode 100644 index 0000000000..9b87995aa7 --- /dev/null +++ b/src/internal/trace/raw/writer.go @@ -0,0 +1,75 @@ +// 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. + +package raw + +import ( + "encoding/binary" + "fmt" + "io" + + "internal/trace/event" + "internal/trace/version" +) + +// Writer emits the wire format of a trace. +// +// It may not produce a byte-for-byte compatible trace from what is +// produced by the runtime, because it may be missing extra padding +// in the LEB128 encoding that the runtime adds but isn't necessary +// when you know the data up-front. +type Writer struct { + w io.Writer + buf []byte + v version.Version + specs []event.Spec +} + +// NewWriter creates a new byte format writer. +func NewWriter(w io.Writer, v version.Version) (*Writer, error) { + _, err := version.WriteHeader(w, v) + return &Writer{w: w, v: v, specs: v.Specs()}, err +} + +// WriteEvent writes a single event to the trace wire format stream. +func (w *Writer) WriteEvent(e Event) error { + // Check version. + if e.Version != w.v { + return fmt.Errorf("mismatched version between writer (go 1.%d) and event (go 1.%d)", w.v, e.Version) + } + + // Write event header byte. + w.buf = append(w.buf, uint8(e.Ev)) + + // Write out all arguments. + spec := w.specs[e.Ev] + for _, arg := range e.Args[:len(spec.Args)] { + w.buf = binary.AppendUvarint(w.buf, arg) + } + if spec.IsStack { + frameArgs := e.Args[len(spec.Args):] + for i := 0; i < len(frameArgs); i++ { + w.buf = binary.AppendUvarint(w.buf, frameArgs[i]) + } + } + + // Write out the length of the data. + if spec.HasData { + w.buf = binary.AppendUvarint(w.buf, uint64(len(e.Data))) + } + + // Write out varint events. + _, err := w.w.Write(w.buf) + w.buf = w.buf[:0] + if err != nil { + return err + } + + // Write out data. + if spec.HasData { + _, err := w.w.Write(e.Data) + return err + } + return nil +} diff --git a/src/internal/trace/reader.go b/src/internal/trace/reader.go new file mode 100644 index 0000000000..c05d5b58b3 --- /dev/null +++ b/src/internal/trace/reader.go @@ -0,0 +1,237 @@ +// 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. + +package trace + +import ( + "bufio" + "fmt" + "io" + "slices" + "strings" + + "internal/trace/event/go122" + "internal/trace/internal/oldtrace" + "internal/trace/version" +) + +// Reader reads a byte stream, validates it, and produces trace events. +type Reader struct { + r *bufio.Reader + lastTs Time + gen *generation + spill *spilledBatch + spillErr error // error from reading spill + frontier []*batchCursor + cpuSamples []cpuSample + order ordering + emittedSync bool + + go121Events *oldTraceConverter +} + +// NewReader creates a new trace reader. +func NewReader(r io.Reader) (*Reader, error) { + br := bufio.NewReader(r) + v, err := version.ReadHeader(br) + if err != nil { + return nil, err + } + switch v { + case version.Go111, version.Go119, version.Go121: + tr, err := oldtrace.Parse(br, v) + if err != nil { + return nil, err + } + return &Reader{ + go121Events: convertOldFormat(tr), + }, nil + case version.Go122, version.Go123: + return &Reader{ + r: br, + order: ordering{ + mStates: make(map[ThreadID]*mState), + pStates: make(map[ProcID]*pState), + gStates: make(map[GoID]*gState), + activeTasks: make(map[TaskID]taskState), + }, + // Don't emit a sync event when we first go to emit events. + emittedSync: true, + }, nil + default: + return nil, fmt.Errorf("unknown or unsupported version go 1.%d", v) + } +} + +// ReadEvent reads a single event from the stream. +// +// If the stream has been exhausted, it returns an invalid +// event and io.EOF. +func (r *Reader) ReadEvent() (e Event, err error) { + if r.go121Events != nil { + ev, err := r.go121Events.next() + if err != nil { + // XXX do we have to emit an EventSync when the trace is done? + return Event{}, err + } + return ev, nil + } + + // Go 1.22+ trace parsing algorithm. + // + // (1) Read in all the batches for the next generation from the stream. + // (a) Use the size field in the header to quickly find all batches. + // (2) Parse out the strings, stacks, CPU samples, and timestamp conversion data. + // (3) Group each event batch by M, sorted by timestamp. (batchCursor contains the groups.) + // (4) Organize batchCursors in a min-heap, ordered by the timestamp of the next event for each M. + // (5) Try to advance the next event for the M at the top of the min-heap. + // (a) On success, select that M. + // (b) On failure, sort the min-heap and try to advance other Ms. Select the first M that advances. + // (c) If there's nothing left to advance, goto (1). + // (6) Select the latest event for the selected M and get it ready to be returned. + // (7) Read the next event for the selected M and update the min-heap. + // (8) Return the selected event, goto (5) on the next call. + + // Set us up to track the last timestamp and fix up + // the timestamp of any event that comes through. + defer func() { + if err != nil { + return + } + if err = e.validateTableIDs(); err != nil { + return + } + if e.base.time <= r.lastTs { + e.base.time = r.lastTs + 1 + } + r.lastTs = e.base.time + }() + + // Consume any events in the ordering first. + if ev, ok := r.order.Next(); ok { + return ev, nil + } + + // Check if we need to refresh the generation. + if len(r.frontier) == 0 && len(r.cpuSamples) == 0 { + if !r.emittedSync { + r.emittedSync = true + return syncEvent(r.gen.evTable, r.lastTs), nil + } + if r.spillErr != nil { + return Event{}, r.spillErr + } + if r.gen != nil && r.spill == nil { + // If we have a generation from the last read, + // and there's nothing left in the frontier, and + // there's no spilled batch, indicating that there's + // no further generation, it means we're done. + // Return io.EOF. + return Event{}, io.EOF + } + // Read the next generation. + var err error + r.gen, r.spill, err = readGeneration(r.r, r.spill) + if r.gen == nil { + return Event{}, err + } + r.spillErr = err + + // Reset CPU samples cursor. + r.cpuSamples = r.gen.cpuSamples + + // Reset frontier. + for m, batches := range r.gen.batches { + bc := &batchCursor{m: m} + ok, err := bc.nextEvent(batches, r.gen.freq) + if err != nil { + return Event{}, err + } + if !ok { + // Turns out there aren't actually any events in these batches. + continue + } + r.frontier = heapInsert(r.frontier, bc) + } + + // Reset emittedSync. + r.emittedSync = false + } + tryAdvance := func(i int) (bool, error) { + bc := r.frontier[i] + + if ok, err := r.order.Advance(&bc.ev, r.gen.evTable, bc.m, r.gen.gen); !ok || err != nil { + return ok, err + } + + // Refresh the cursor's event. + ok, err := bc.nextEvent(r.gen.batches[bc.m], r.gen.freq) + if err != nil { + return false, err + } + if ok { + // If we successfully refreshed, update the heap. + heapUpdate(r.frontier, i) + } else { + // There's nothing else to read. Delete this cursor from the frontier. + r.frontier = heapRemove(r.frontier, i) + } + return true, nil + } + // Inject a CPU sample if it comes next. + if len(r.cpuSamples) != 0 { + if len(r.frontier) == 0 || r.cpuSamples[0].time < r.frontier[0].ev.time { + e := r.cpuSamples[0].asEvent(r.gen.evTable) + r.cpuSamples = r.cpuSamples[1:] + return e, nil + } + } + // Try to advance the head of the frontier, which should have the minimum timestamp. + // This should be by far the most common case + if len(r.frontier) == 0 { + return Event{}, fmt.Errorf("broken trace: frontier is empty:\n[gen=%d]\n\n%s\n%s\n", r.gen.gen, dumpFrontier(r.frontier), dumpOrdering(&r.order)) + } + if ok, err := tryAdvance(0); err != nil { + return Event{}, err + } else if !ok { + // Try to advance the rest of the frontier, in timestamp order. + // + // To do this, sort the min-heap. A sorted min-heap is still a + // min-heap, but now we can iterate over the rest and try to + // advance in order. This path should be rare. + slices.SortFunc(r.frontier, (*batchCursor).compare) + success := false + for i := 1; i < len(r.frontier); i++ { + if ok, err = tryAdvance(i); err != nil { + return Event{}, err + } else if ok { + success = true + break + } + } + if !success { + return Event{}, fmt.Errorf("broken trace: failed to advance: frontier:\n[gen=%d]\n\n%s\n%s\n", r.gen.gen, dumpFrontier(r.frontier), dumpOrdering(&r.order)) + } + } + + // Pick off the next event on the queue. At this point, one must exist. + ev, ok := r.order.Next() + if !ok { + panic("invariant violation: advance successful, but queue is empty") + } + return ev, nil +} + +func dumpFrontier(frontier []*batchCursor) string { + var sb strings.Builder + for _, bc := range frontier { + spec := go122.Specs()[bc.ev.typ] + fmt.Fprintf(&sb, "M %d [%s time=%d", bc.m, spec.Name, bc.ev.time) + for i, arg := range spec.Args[1:] { + fmt.Fprintf(&sb, " %s=%d", arg, bc.ev.args[i]) + } + fmt.Fprintf(&sb, "]\n") + } + return sb.String() +} diff --git a/src/internal/trace/reader_test.go b/src/internal/trace/reader_test.go new file mode 100644 index 0000000000..37c21da667 --- /dev/null +++ b/src/internal/trace/reader_test.go @@ -0,0 +1,172 @@ +// 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. + +package trace_test + +import ( + "bytes" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "testing" + + "internal/trace" + "internal/trace/raw" + "internal/trace/testtrace" + "internal/trace/version" +) + +var ( + logEvents = flag.Bool("log-events", false, "whether to log high-level events; significantly slows down tests") + dumpTraces = flag.Bool("dump-traces", false, "dump traces even on success") +) + +func TestReaderGolden(t *testing.T) { + matches, err := filepath.Glob("./testdata/tests/*.test") + if err != nil { + t.Fatalf("failed to glob for tests: %v", err) + } + for _, testPath := range matches { + testPath := testPath + testName, err := filepath.Rel("./testdata", testPath) + if err != nil { + t.Fatalf("failed to relativize testdata path: %v", err) + } + t.Run(testName, func(t *testing.T) { + tr, exp, err := testtrace.ParseFile(testPath) + if err != nil { + t.Fatalf("failed to parse test file at %s: %v", testPath, err) + } + testReader(t, tr, exp) + }) + } +} + +func FuzzReader(f *testing.F) { + // Currently disabled because the parser doesn't do much validation and most + // getters can be made to panic. Turn this on once the parser is meant to + // reject invalid traces. + const testGetters = false + + f.Fuzz(func(t *testing.T, b []byte) { + r, err := trace.NewReader(bytes.NewReader(b)) + if err != nil { + return + } + for { + ev, err := r.ReadEvent() + if err != nil { + break + } + + if !testGetters { + continue + } + // Make sure getters don't do anything that panics + switch ev.Kind() { + case trace.EventLabel: + ev.Label() + case trace.EventLog: + ev.Log() + case trace.EventMetric: + ev.Metric() + case trace.EventRangeActive, trace.EventRangeBegin: + ev.Range() + case trace.EventRangeEnd: + ev.Range() + ev.RangeAttributes() + case trace.EventStateTransition: + ev.StateTransition() + case trace.EventRegionBegin, trace.EventRegionEnd: + ev.Region() + case trace.EventTaskBegin, trace.EventTaskEnd: + ev.Task() + case trace.EventSync: + case trace.EventStackSample: + case trace.EventBad: + } + } + }) +} + +func testReader(t *testing.T, tr io.Reader, exp *testtrace.Expectation) { + r, err := trace.NewReader(tr) + if err != nil { + if err := exp.Check(err); err != nil { + t.Error(err) + } + return + } + v := testtrace.NewValidator() + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + if err := exp.Check(err); err != nil { + t.Error(err) + } + return + } + if *logEvents { + t.Log(ev.String()) + } + if err := v.Event(ev); err != nil { + t.Error(err) + } + } + if err := exp.Check(nil); err != nil { + t.Error(err) + } +} + +func dumpTraceToText(t *testing.T, b []byte) string { + t.Helper() + + br, err := raw.NewReader(bytes.NewReader(b)) + if err != nil { + t.Fatalf("dumping trace: %v", err) + } + var sb strings.Builder + tw, err := raw.NewTextWriter(&sb, version.Current) + if err != nil { + t.Fatalf("dumping trace: %v", err) + } + for { + ev, err := br.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("dumping trace: %v", err) + } + if err := tw.WriteEvent(ev); err != nil { + t.Fatalf("dumping trace: %v", err) + } + } + return sb.String() +} + +func dumpTraceToFile(t *testing.T, testName string, stress bool, b []byte) string { + t.Helper() + + desc := "default" + if stress { + desc = "stress" + } + name := fmt.Sprintf("%s.%s.trace.", testName, desc) + f, err := os.CreateTemp("", name) + if err != nil { + t.Fatalf("creating temp file: %v", err) + } + defer f.Close() + if _, err := io.Copy(f, bytes.NewReader(b)); err != nil { + t.Fatalf("writing trace dump to %q: %v", f.Name(), err) + } + return f.Name() +} diff --git a/src/internal/trace/resources.go b/src/internal/trace/resources.go new file mode 100644 index 0000000000..f49696f91c --- /dev/null +++ b/src/internal/trace/resources.go @@ -0,0 +1,274 @@ +// 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. + +package trace + +import "fmt" + +// ThreadID is the runtime-internal M structure's ID. This is unique +// for each OS thread. +type ThreadID int64 + +// NoThread indicates that the relevant events don't correspond to any +// thread in particular. +const NoThread = ThreadID(-1) + +// ProcID is the runtime-internal G structure's id field. This is unique +// for each P. +type ProcID int64 + +// NoProc indicates that the relevant events don't correspond to any +// P in particular. +const NoProc = ProcID(-1) + +// GoID is the runtime-internal G structure's goid field. This is unique +// for each goroutine. +type GoID int64 + +// NoGoroutine indicates that the relevant events don't correspond to any +// goroutine in particular. +const NoGoroutine = GoID(-1) + +// GoState represents the state of a goroutine. +// +// New GoStates may be added in the future. Users of this type must be robust +// to that possibility. +type GoState uint8 + +const ( + GoUndetermined GoState = iota // No information is known about the goroutine. + GoNotExist // Goroutine does not exist. + GoRunnable // Goroutine is runnable but not running. + GoRunning // Goroutine is running. + GoWaiting // Goroutine is waiting on something to happen. + GoSyscall // Goroutine is in a system call. +) + +// Executing returns true if the state indicates that the goroutine is executing +// and bound to its thread. +func (s GoState) Executing() bool { + return s == GoRunning || s == GoSyscall +} + +// String returns a human-readable representation of a GoState. +// +// The format of the returned string is for debugging purposes and is subject to change. +func (s GoState) String() string { + switch s { + case GoUndetermined: + return "Undetermined" + case GoNotExist: + return "NotExist" + case GoRunnable: + return "Runnable" + case GoRunning: + return "Running" + case GoWaiting: + return "Waiting" + case GoSyscall: + return "Syscall" + } + return "Bad" +} + +// ProcState represents the state of a proc. +// +// New ProcStates may be added in the future. Users of this type must be robust +// to that possibility. +type ProcState uint8 + +const ( + ProcUndetermined ProcState = iota // No information is known about the proc. + ProcNotExist // Proc does not exist. + ProcRunning // Proc is running. + ProcIdle // Proc is idle. +) + +// Executing returns true if the state indicates that the proc is executing +// and bound to its thread. +func (s ProcState) Executing() bool { + return s == ProcRunning +} + +// String returns a human-readable representation of a ProcState. +// +// The format of the returned string is for debugging purposes and is subject to change. +func (s ProcState) String() string { + switch s { + case ProcUndetermined: + return "Undetermined" + case ProcNotExist: + return "NotExist" + case ProcRunning: + return "Running" + case ProcIdle: + return "Idle" + } + return "Bad" +} + +// ResourceKind indicates a kind of resource that has a state machine. +// +// New ResourceKinds may be added in the future. Users of this type must be robust +// to that possibility. +type ResourceKind uint8 + +const ( + ResourceNone ResourceKind = iota // No resource. + ResourceGoroutine // Goroutine. + ResourceProc // Proc. + ResourceThread // Thread. +) + +// String returns a human-readable representation of a ResourceKind. +// +// The format of the returned string is for debugging purposes and is subject to change. +func (r ResourceKind) String() string { + switch r { + case ResourceNone: + return "None" + case ResourceGoroutine: + return "Goroutine" + case ResourceProc: + return "Proc" + case ResourceThread: + return "Thread" + } + return "Bad" +} + +// ResourceID represents a generic resource ID. +type ResourceID struct { + // Kind is the kind of resource this ID is for. + Kind ResourceKind + id int64 +} + +// MakeResourceID creates a general resource ID from a specific resource's ID. +func MakeResourceID[T interface{ GoID | ProcID | ThreadID }](id T) ResourceID { + var rd ResourceID + var a any = id + switch a.(type) { + case GoID: + rd.Kind = ResourceGoroutine + case ProcID: + rd.Kind = ResourceProc + case ThreadID: + rd.Kind = ResourceThread + } + rd.id = int64(id) + return rd +} + +// Goroutine obtains a GoID from the resource ID. +// +// r.Kind must be ResourceGoroutine or this function will panic. +func (r ResourceID) Goroutine() GoID { + if r.Kind != ResourceGoroutine { + panic(fmt.Sprintf("attempted to get GoID from %s resource ID", r.Kind)) + } + return GoID(r.id) +} + +// Proc obtains a ProcID from the resource ID. +// +// r.Kind must be ResourceProc or this function will panic. +func (r ResourceID) Proc() ProcID { + if r.Kind != ResourceProc { + panic(fmt.Sprintf("attempted to get ProcID from %s resource ID", r.Kind)) + } + return ProcID(r.id) +} + +// Thread obtains a ThreadID from the resource ID. +// +// r.Kind must be ResourceThread or this function will panic. +func (r ResourceID) Thread() ThreadID { + if r.Kind != ResourceThread { + panic(fmt.Sprintf("attempted to get ThreadID from %s resource ID", r.Kind)) + } + return ThreadID(r.id) +} + +// String returns a human-readable string representation of the ResourceID. +// +// This representation is subject to change and is intended primarily for debugging. +func (r ResourceID) String() string { + if r.Kind == ResourceNone { + return r.Kind.String() + } + return fmt.Sprintf("%s(%d)", r.Kind, r.id) +} + +// StateTransition provides details about a StateTransition event. +type StateTransition struct { + // Resource is the resource this state transition is for. + Resource ResourceID + + // Reason is a human-readable reason for the state transition. + Reason string + + // Stack is the stack trace of the resource making the state transition. + // + // This is distinct from the result (Event).Stack because it pertains to + // the transitioning resource, not any of the ones executing the event + // this StateTransition came from. + // + // An example of this difference is the NotExist -> Runnable transition for + // goroutines, which indicates goroutine creation. In this particular case, + // a Stack here would refer to the starting stack of the new goroutine, and + // an (Event).Stack would refer to the stack trace of whoever created the + // goroutine. + Stack Stack + + // The actual transition data. Stored in a neutral form so that + // we don't need fields for every kind of resource. + id int64 + oldState uint8 + newState uint8 +} + +func goStateTransition(id GoID, from, to GoState) StateTransition { + return StateTransition{ + Resource: ResourceID{Kind: ResourceGoroutine, id: int64(id)}, + oldState: uint8(from), + newState: uint8(to), + } +} + +func procStateTransition(id ProcID, from, to ProcState) StateTransition { + return StateTransition{ + Resource: ResourceID{Kind: ResourceProc, id: int64(id)}, + oldState: uint8(from), + newState: uint8(to), + } +} + +// Goroutine returns the state transition for a goroutine. +// +// Transitions to and from states that are Executing are special in that +// they change the future execution context. In other words, future events +// on the same thread will feature the same goroutine until it stops running. +// +// Panics if d.Resource.Kind is not ResourceGoroutine. +func (d StateTransition) Goroutine() (from, to GoState) { + if d.Resource.Kind != ResourceGoroutine { + panic("Goroutine called on non-Goroutine state transition") + } + return GoState(d.oldState), GoState(d.newState) +} + +// Proc returns the state transition for a proc. +// +// Transitions to and from states that are Executing are special in that +// they change the future execution context. In other words, future events +// on the same thread will feature the same goroutine until it stops running. +// +// Panics if d.Resource.Kind is not ResourceProc. +func (d StateTransition) Proc() (from, to ProcState) { + if d.Resource.Kind != ResourceProc { + panic("Proc called on non-Proc state transition") + } + return ProcState(d.oldState), ProcState(d.newState) +} diff --git a/src/internal/trace/summary.go b/src/internal/trace/summary.go index 6d889baca3..8dd2317d86 100644 --- a/src/internal/trace/summary.go +++ b/src/internal/trace/summary.go @@ -5,7 +5,6 @@ package trace import ( - tracev2 "internal/trace/v2" "sort" "strings" "time" @@ -13,19 +12,19 @@ import ( // Summary is the analysis result produced by the summarizer. type Summary struct { - Goroutines map[tracev2.GoID]*GoroutineSummary - Tasks map[tracev2.TaskID]*UserTaskSummary + Goroutines map[GoID]*GoroutineSummary + Tasks map[TaskID]*UserTaskSummary } // GoroutineSummary contains statistics and execution details of a single goroutine. // (For v2 traces.) type GoroutineSummary struct { - ID tracev2.GoID - Name string // A non-unique human-friendly identifier for the goroutine. - PC uint64 // The first PC we saw for the entry function of the goroutine - CreationTime tracev2.Time // Timestamp of the first appearance in the trace. - StartTime tracev2.Time // Timestamp of the first time it started running. 0 if the goroutine never ran. - EndTime tracev2.Time // Timestamp of when the goroutine exited. 0 if the goroutine never exited. + ID GoID + Name string // A non-unique human-friendly identifier for the goroutine. + PC uint64 // The first PC we saw for the entry function of the goroutine + CreationTime Time // Timestamp of the first appearance in the trace. + StartTime Time // Timestamp of the first time it started running. 0 if the goroutine never ran. + EndTime Time // Timestamp of when the goroutine exited. 0 if the goroutine never exited. // List of regions in the goroutine, sorted based on the start time. Regions []*UserRegionSummary @@ -43,25 +42,25 @@ type GoroutineSummary struct { // UserTaskSummary represents a task in the trace. type UserTaskSummary struct { - ID tracev2.TaskID + ID TaskID Name string Parent *UserTaskSummary // nil if the parent is unknown. Children []*UserTaskSummary // Task begin event. An EventTaskBegin event or nil. - Start *tracev2.Event + Start *Event // End end event. Normally EventTaskEnd event or nil. - End *tracev2.Event + End *Event - // Logs is a list of tracev2.EventLog events associated with the task. - Logs []*tracev2.Event + // Logs is a list of EventLog events associated with the task. + Logs []*Event // List of regions in the task, sorted based on the start time. Regions []*UserRegionSummary // Goroutines is the set of goroutines associated with this task. - Goroutines map[tracev2.GoID]*GoroutineSummary + Goroutines map[GoID]*GoroutineSummary } // Complete returns true if we have complete information about the task @@ -83,19 +82,19 @@ func (s *UserTaskSummary) Descendents() []*UserTaskSummary { // UserRegionSummary represents a region and goroutine execution stats // while the region was active. (For v2 traces.) type UserRegionSummary struct { - TaskID tracev2.TaskID + TaskID TaskID Name string // Region start event. Normally EventRegionBegin event or nil, // but can be a state transition event from NotExist or Undetermined // if the region is a synthetic region representing task inheritance // from the parent goroutine. - Start *tracev2.Event + Start *Event // Region end event. Normally EventRegionEnd event or nil, // but can be a state transition event to NotExist if the goroutine // terminated without explicitly ending the region. - End *tracev2.Event + End *Event GoroutineExecStats } @@ -183,7 +182,7 @@ func (s GoroutineExecStats) clone() (r GoroutineExecStats) { // snapshotStat returns the snapshot of the goroutine execution statistics. // This is called as we process the ordered trace event stream. lastTs is used // to process pending statistics if this is called before any goroutine end event. -func (g *GoroutineSummary) snapshotStat(lastTs tracev2.Time) (ret GoroutineExecStats) { +func (g *GoroutineSummary) snapshotStat(lastTs Time) (ret GoroutineExecStats) { ret = g.GoroutineExecStats.clone() if g.goroutineSummary == nil { @@ -220,7 +219,7 @@ func (g *GoroutineSummary) snapshotStat(lastTs tracev2.Time) (ret GoroutineExecS // finalize is called when processing a goroutine end event or at // the end of trace processing. This finalizes the execution stat // and any active regions in the goroutine, in which case trigger is nil. -func (g *GoroutineSummary) finalize(lastTs tracev2.Time, trigger *tracev2.Event) { +func (g *GoroutineSummary) finalize(lastTs Time, trigger *Event) { if trigger != nil { g.EndTime = trigger.Time() } @@ -244,57 +243,57 @@ func (g *GoroutineSummary) finalize(lastTs tracev2.Time, trigger *tracev2.Event) // goroutineSummary is a private part of GoroutineSummary that is required only during analysis. type goroutineSummary struct { - lastStartTime tracev2.Time - lastRunnableTime tracev2.Time - lastBlockTime tracev2.Time + lastStartTime Time + lastRunnableTime Time + lastBlockTime Time lastBlockReason string - lastSyscallTime tracev2.Time - lastSyscallBlockTime tracev2.Time - lastRangeTime map[string]tracev2.Time + lastSyscallTime Time + lastSyscallBlockTime Time + lastRangeTime map[string]Time activeRegions []*UserRegionSummary // stack of active regions } // Summarizer constructs per-goroutine time statistics for v2 traces. type Summarizer struct { // gs contains the map of goroutine summaries we're building up to return to the caller. - gs map[tracev2.GoID]*GoroutineSummary + gs map[GoID]*GoroutineSummary // tasks contains the map of task summaries we're building up to return to the caller. - tasks map[tracev2.TaskID]*UserTaskSummary + tasks map[TaskID]*UserTaskSummary // syscallingP and syscallingG represent a binding between a P and G in a syscall. // Used to correctly identify and clean up after syscalls (blocking or otherwise). - syscallingP map[tracev2.ProcID]tracev2.GoID - syscallingG map[tracev2.GoID]tracev2.ProcID + syscallingP map[ProcID]GoID + syscallingG map[GoID]ProcID // rangesP is used for optimistic tracking of P-based ranges for goroutines. // // It's a best-effort mapping of an active range on a P to the goroutine we think // is associated with it. - rangesP map[rangeP]tracev2.GoID + rangesP map[rangeP]GoID - lastTs tracev2.Time // timestamp of the last event processed. - syncTs tracev2.Time // timestamp of the last sync event processed (or the first timestamp in the trace). + lastTs Time // timestamp of the last event processed. + syncTs Time // timestamp of the last sync event processed (or the first timestamp in the trace). } // NewSummarizer creates a new struct to build goroutine stats from a trace. func NewSummarizer() *Summarizer { return &Summarizer{ - gs: make(map[tracev2.GoID]*GoroutineSummary), - tasks: make(map[tracev2.TaskID]*UserTaskSummary), - syscallingP: make(map[tracev2.ProcID]tracev2.GoID), - syscallingG: make(map[tracev2.GoID]tracev2.ProcID), - rangesP: make(map[rangeP]tracev2.GoID), + gs: make(map[GoID]*GoroutineSummary), + tasks: make(map[TaskID]*UserTaskSummary), + syscallingP: make(map[ProcID]GoID), + syscallingG: make(map[GoID]ProcID), + rangesP: make(map[rangeP]GoID), } } type rangeP struct { - id tracev2.ProcID + id ProcID name string } // Event feeds a single event into the stats summarizer. -func (s *Summarizer) Event(ev *tracev2.Event) { +func (s *Summarizer) Event(ev *Event) { if s.syncTs == 0 { s.syncTs = ev.Time() } @@ -302,15 +301,15 @@ func (s *Summarizer) Event(ev *tracev2.Event) { switch ev.Kind() { // Record sync time for the RangeActive events. - case tracev2.EventSync: + case EventSync: s.syncTs = ev.Time() // Handle state transitions. - case tracev2.EventStateTransition: + case EventStateTransition: st := ev.StateTransition() switch st.Resource.Kind { // Handle goroutine transitions, which are the meat of this computation. - case tracev2.ResourceGoroutine: + case ResourceGoroutine: id := st.Resource.Goroutine() old, new := st.Goroutine() if old == new { @@ -321,17 +320,17 @@ func (s *Summarizer) Event(ev *tracev2.Event) { // Handle transition out. g := s.gs[id] switch old { - case tracev2.GoUndetermined, tracev2.GoNotExist: + case GoUndetermined, GoNotExist: g = &GoroutineSummary{ID: id, goroutineSummary: &goroutineSummary{}} // If we're coming out of GoUndetermined, then the creation time is the // time of the last sync. - if old == tracev2.GoUndetermined { + if old == GoUndetermined { g.CreationTime = s.syncTs } else { g.CreationTime = ev.Time() } // The goroutine is being created, or it's being named for the first time. - g.lastRangeTime = make(map[string]tracev2.Time) + g.lastRangeTime = make(map[string]Time) g.BlockTimeByReason = make(map[string]time.Duration) g.RangeTime = make(map[string]time.Duration) @@ -351,23 +350,23 @@ func (s *Summarizer) Event(ev *tracev2.Event) { g.activeRegions = []*UserRegionSummary{{TaskID: s.TaskID, Start: ev}} } s.gs[g.ID] = g - case tracev2.GoRunning: + case GoRunning: // Record execution time as we transition out of running g.ExecTime += ev.Time().Sub(g.lastStartTime) g.lastStartTime = 0 - case tracev2.GoWaiting: + case GoWaiting: // Record block time as we transition out of waiting. if g.lastBlockTime != 0 { g.BlockTimeByReason[g.lastBlockReason] += ev.Time().Sub(g.lastBlockTime) g.lastBlockTime = 0 } - case tracev2.GoRunnable: + case GoRunnable: // Record sched latency time as we transition out of runnable. if g.lastRunnableTime != 0 { g.SchedWaitTime += ev.Time().Sub(g.lastRunnableTime) g.lastRunnableTime = 0 } - case tracev2.GoSyscall: + case GoSyscall: // Record syscall execution time and syscall block time as we transition out of syscall. if g.lastSyscallTime != 0 { if g.lastSyscallBlockTime != 0 { @@ -391,10 +390,10 @@ func (s *Summarizer) Event(ev *tracev2.Event) { // goroutine, because it represents its immutable start point. if g.Name == "" { stk := st.Stack - if stk != tracev2.NoStack { - var frame tracev2.StackFrame + if stk != NoStack { + var frame StackFrame var ok bool - stk.Frames(func(f tracev2.StackFrame) bool { + stk.Frames(func(f StackFrame) bool { frame = f ok = true return true @@ -413,15 +412,15 @@ func (s *Summarizer) Event(ev *tracev2.Event) { // Handle transition in. switch new { - case tracev2.GoRunning: + case GoRunning: // We started running. Record it. g.lastStartTime = ev.Time() if g.StartTime == 0 { g.StartTime = ev.Time() } - case tracev2.GoRunnable: + case GoRunnable: g.lastRunnableTime = ev.Time() - case tracev2.GoWaiting: + case GoWaiting: if st.Reason != "forever" { g.lastBlockTime = ev.Time() g.lastBlockReason = st.Reason @@ -429,9 +428,9 @@ func (s *Summarizer) Event(ev *tracev2.Event) { } // "Forever" is like goroutine death. fallthrough - case tracev2.GoNotExist: + case GoNotExist: g.finalize(ev.Time(), ev) - case tracev2.GoSyscall: + case GoSyscall: s.syscallingP[ev.Proc()] = id s.syscallingG[id] = ev.Proc() g.lastSyscallTime = ev.Time() @@ -439,10 +438,10 @@ func (s *Summarizer) Event(ev *tracev2.Event) { // Handle procs to detect syscall blocking, which si identifiable as a // proc going idle while the goroutine it was attached to is in a syscall. - case tracev2.ResourceProc: + case ResourceProc: id := st.Resource.Proc() old, new := st.Proc() - if old != new && new == tracev2.ProcIdle { + if old != new && new == ProcIdle { if goid, ok := s.syscallingP[id]; ok { g := s.gs[goid] g.lastSyscallBlockTime = ev.Time() @@ -452,18 +451,18 @@ func (s *Summarizer) Event(ev *tracev2.Event) { } // Handle ranges of all kinds. - case tracev2.EventRangeBegin, tracev2.EventRangeActive: + case EventRangeBegin, EventRangeActive: r := ev.Range() var g *GoroutineSummary switch r.Scope.Kind { - case tracev2.ResourceGoroutine: + case ResourceGoroutine: // Simple goroutine range. We attribute the entire range regardless of // goroutine stats. Lots of situations are still identifiable, e.g. a // goroutine blocked often in mark assist will have both high mark assist // and high block times. Those interested in a deeper view can look at the // trace viewer. g = s.gs[r.Scope.Goroutine()] - case tracev2.ResourceProc: + case ResourceProc: // N.B. These ranges are not actually bound to the goroutine, they're // bound to the P. But if we happen to be on the P the whole time, let's // try to attribute it to the goroutine. (e.g. GC sweeps are here.) @@ -475,7 +474,7 @@ func (s *Summarizer) Event(ev *tracev2.Event) { if g == nil { break } - if ev.Kind() == tracev2.EventRangeActive { + if ev.Kind() == EventRangeActive { if ts := g.lastRangeTime[r.Name]; ts != 0 { g.RangeTime[r.Name] += s.syncTs.Sub(ts) } @@ -483,13 +482,13 @@ func (s *Summarizer) Event(ev *tracev2.Event) { } else { g.lastRangeTime[r.Name] = ev.Time() } - case tracev2.EventRangeEnd: + case EventRangeEnd: r := ev.Range() var g *GoroutineSummary switch r.Scope.Kind { - case tracev2.ResourceGoroutine: + case ResourceGoroutine: g = s.gs[r.Scope.Goroutine()] - case tracev2.ResourceProc: + case ResourceProc: rp := rangeP{id: r.Scope.Proc(), name: r.Name} if goid, ok := s.rangesP[rp]; ok { if goid == ev.Goroutine() { @@ -511,7 +510,7 @@ func (s *Summarizer) Event(ev *tracev2.Event) { delete(g.lastRangeTime, r.Name) // Handle user-defined regions. - case tracev2.EventRegionBegin: + case EventRegionBegin: g := s.gs[ev.Goroutine()] r := ev.Region() region := &UserRegionSummary{ @@ -525,7 +524,7 @@ func (s *Summarizer) Event(ev *tracev2.Event) { task := s.getOrAddTask(r.Task) task.Regions = append(task.Regions, region) task.Goroutines[g.ID] = g - case tracev2.EventRegionEnd: + case EventRegionEnd: g := s.gs[ev.Goroutine()] r := ev.Region() var sd *UserRegionSummary @@ -549,13 +548,13 @@ func (s *Summarizer) Event(ev *tracev2.Event) { g.Regions = append(g.Regions, sd) // Handle tasks and logs. - case tracev2.EventTaskBegin, tracev2.EventTaskEnd: + case EventTaskBegin, EventTaskEnd: // Initialize the task. t := ev.Task() task := s.getOrAddTask(t.ID) task.Name = t.Type task.Goroutines[ev.Goroutine()] = s.gs[ev.Goroutine()] - if ev.Kind() == tracev2.EventTaskBegin { + if ev.Kind() == EventTaskBegin { task.Start = ev } else { task.End = ev @@ -563,12 +562,12 @@ func (s *Summarizer) Event(ev *tracev2.Event) { // Initialize the parent, if one exists and it hasn't been done yet. // We need to avoid doing it twice, otherwise we could appear twice // in the parent's Children list. - if t.Parent != tracev2.NoTask && task.Parent == nil { + if t.Parent != NoTask && task.Parent == nil { parent := s.getOrAddTask(t.Parent) task.Parent = parent parent.Children = append(parent.Children, task) } - case tracev2.EventLog: + case EventLog: log := ev.Log() // Just add the log to the task. We'll create the task if it // doesn't exist (it's just been mentioned now). @@ -578,10 +577,10 @@ func (s *Summarizer) Event(ev *tracev2.Event) { } } -func (s *Summarizer) getOrAddTask(id tracev2.TaskID) *UserTaskSummary { +func (s *Summarizer) getOrAddTask(id TaskID) *UserTaskSummary { task := s.tasks[id] if task == nil { - task = &UserTaskSummary{ID: id, Goroutines: make(map[tracev2.GoID]*GoroutineSummary)} + task = &UserTaskSummary{ID: id, Goroutines: make(map[GoID]*GoroutineSummary)} s.tasks[id] = task } return task @@ -616,30 +615,30 @@ func (s *Summarizer) Finalize() *Summary { // RelatedGoroutinesV2 finds a set of goroutines related to goroutine goid for v2 traces. // The association is based on whether they have synchronized with each other in the Go // scheduler (one has unblocked another). -func RelatedGoroutinesV2(events []tracev2.Event, goid tracev2.GoID) map[tracev2.GoID]struct{} { +func RelatedGoroutinesV2(events []Event, goid GoID) map[GoID]struct{} { // Process all the events, looking for transitions of goroutines // out of GoWaiting. If there was an active goroutine when this // happened, then we know that active goroutine unblocked another. // Scribble all these down so we can process them. type unblockEdge struct { - operator tracev2.GoID - operand tracev2.GoID + operator GoID + operand GoID } var unblockEdges []unblockEdge for _, ev := range events { - if ev.Goroutine() == tracev2.NoGoroutine { + if ev.Goroutine() == NoGoroutine { continue } - if ev.Kind() != tracev2.EventStateTransition { + if ev.Kind() != EventStateTransition { continue } st := ev.StateTransition() - if st.Resource.Kind != tracev2.ResourceGoroutine { + if st.Resource.Kind != ResourceGoroutine { continue } id := st.Resource.Goroutine() old, new := st.Goroutine() - if old == new || old != tracev2.GoWaiting { + if old == new || old != GoWaiting { continue } unblockEdges = append(unblockEdges, unblockEdge{ @@ -649,11 +648,11 @@ func RelatedGoroutinesV2(events []tracev2.Event, goid tracev2.GoID) map[tracev2. } // Compute the transitive closure of depth 2 of goroutines that have unblocked each other // (starting from goid). - gmap := make(map[tracev2.GoID]struct{}) + gmap := make(map[GoID]struct{}) gmap[goid] = struct{}{} for i := 0; i < 2; i++ { // Copy the map. - gmap1 := make(map[tracev2.GoID]struct{}) + gmap1 := make(map[GoID]struct{}) for g := range gmap { gmap1[g] = struct{}{} } diff --git a/src/internal/trace/summary_test.go b/src/internal/trace/summary_test.go index 9978b57d98..73865652eb 100644 --- a/src/internal/trace/summary_test.go +++ b/src/internal/trace/summary_test.go @@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package trace +package trace_test import ( - tracev2 "internal/trace/v2" - "internal/trace/v2/testtrace" + "internal/trace" + "internal/trace/testtrace" "io" "testing" ) func TestSummarizeGoroutinesTrace(t *testing.T) { - summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-gc-stress.test").Goroutines + summaries := summarizeTraceTest(t, "testdata/tests/go122-gc-stress.test").Goroutines var ( hasSchedWaitTime bool hasSyncBlockTime bool @@ -44,22 +44,22 @@ func TestSummarizeGoroutinesTrace(t *testing.T) { } func TestSummarizeGoroutinesRegionsTrace(t *testing.T) { - summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-annotations.test").Goroutines + summaries := summarizeTraceTest(t, "testdata/tests/go122-annotations.test").Goroutines type region struct { - startKind tracev2.EventKind - endKind tracev2.EventKind + startKind trace.EventKind + endKind trace.EventKind } wantRegions := map[string]region{ // N.B. "pre-existing region" never even makes it into the trace. // // TODO(mknyszek): Add test case for end-without-a-start, which can happen at // a generation split only. - "": {tracev2.EventStateTransition, tracev2.EventStateTransition}, // Task inheritance marker. - "task0 region": {tracev2.EventRegionBegin, tracev2.EventBad}, - "region0": {tracev2.EventRegionBegin, tracev2.EventRegionEnd}, - "region1": {tracev2.EventRegionBegin, tracev2.EventRegionEnd}, - "unended region": {tracev2.EventRegionBegin, tracev2.EventStateTransition}, - "post-existing region": {tracev2.EventRegionBegin, tracev2.EventBad}, + "": {trace.EventStateTransition, trace.EventStateTransition}, // Task inheritance marker. + "task0 region": {trace.EventRegionBegin, trace.EventBad}, + "region0": {trace.EventRegionBegin, trace.EventRegionEnd}, + "region1": {trace.EventRegionBegin, trace.EventRegionEnd}, + "unended region": {trace.EventRegionBegin, trace.EventStateTransition}, + "post-existing region": {trace.EventRegionBegin, trace.EventBad}, } for _, summary := range summaries { basicGoroutineSummaryChecks(t, summary) @@ -78,69 +78,69 @@ func TestSummarizeGoroutinesRegionsTrace(t *testing.T) { } func TestSummarizeTasksTrace(t *testing.T) { - summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-annotations-stress.test").Tasks + summaries := summarizeTraceTest(t, "testdata/tests/go122-annotations-stress.test").Tasks type task struct { name string - parent *tracev2.TaskID - children []tracev2.TaskID - logs []tracev2.Log - goroutines []tracev2.GoID + parent *trace.TaskID + children []trace.TaskID + logs []trace.Log + goroutines []trace.GoID } - parent := func(id tracev2.TaskID) *tracev2.TaskID { - p := new(tracev2.TaskID) + parent := func(id trace.TaskID) *trace.TaskID { + p := new(trace.TaskID) *p = id return p } - wantTasks := map[tracev2.TaskID]task{ - tracev2.BackgroundTask: { + wantTasks := map[trace.TaskID]task{ + trace.BackgroundTask: { // The background task (0) is never any task's parent. - logs: []tracev2.Log{ - {Task: tracev2.BackgroundTask, Category: "log", Message: "before do"}, - {Task: tracev2.BackgroundTask, Category: "log", Message: "before do"}, + logs: []trace.Log{ + {Task: trace.BackgroundTask, Category: "log", Message: "before do"}, + {Task: trace.BackgroundTask, Category: "log", Message: "before do"}, }, - goroutines: []tracev2.GoID{1}, + goroutines: []trace.GoID{1}, }, 1: { // This started before tracing started and has no parents. // Task 2 is technically a child, but we lost that information. - children: []tracev2.TaskID{3, 7, 16}, - logs: []tracev2.Log{ + children: []trace.TaskID{3, 7, 16}, + logs: []trace.Log{ {Task: 1, Category: "log", Message: "before do"}, {Task: 1, Category: "log", Message: "before do"}, }, - goroutines: []tracev2.GoID{1}, + goroutines: []trace.GoID{1}, }, 2: { // This started before tracing started and its parent is technically (1), but that information was lost. - children: []tracev2.TaskID{8, 17}, - logs: []tracev2.Log{ + children: []trace.TaskID{8, 17}, + logs: []trace.Log{ {Task: 2, Category: "log", Message: "before do"}, {Task: 2, Category: "log", Message: "before do"}, }, - goroutines: []tracev2.GoID{1}, + goroutines: []trace.GoID{1}, }, 3: { parent: parent(1), - children: []tracev2.TaskID{10, 19}, - logs: []tracev2.Log{ + children: []trace.TaskID{10, 19}, + logs: []trace.Log{ {Task: 3, Category: "log", Message: "before do"}, {Task: 3, Category: "log", Message: "before do"}, }, - goroutines: []tracev2.GoID{1}, + goroutines: []trace.GoID{1}, }, 4: { // Explicitly, no parent. - children: []tracev2.TaskID{12, 21}, - logs: []tracev2.Log{ + children: []trace.TaskID{12, 21}, + logs: []trace.Log{ {Task: 4, Category: "log", Message: "before do"}, {Task: 4, Category: "log", Message: "before do"}, }, - goroutines: []tracev2.GoID{1}, + goroutines: []trace.GoID{1}, }, 12: { parent: parent(4), - children: []tracev2.TaskID{13}, - logs: []tracev2.Log{ + children: []trace.TaskID{13}, + logs: []trace.Log{ // TODO(mknyszek): This is computed asynchronously in the trace, // which makes regenerating this test very annoying, since it will // likely break this test. Resolve this by making the order not matter. @@ -152,15 +152,15 @@ func TestSummarizeTasksTrace(t *testing.T) { {Task: 12, Category: "log", Message: "before do"}, {Task: 12, Category: "log", Message: "fanout region3"}, }, - goroutines: []tracev2.GoID{1, 5, 6, 7, 8, 9}, + goroutines: []trace.GoID{1, 5, 6, 7, 8, 9}, }, 13: { // Explicitly, no children. parent: parent(12), - logs: []tracev2.Log{ + logs: []trace.Log{ {Task: 13, Category: "log2", Message: "do"}, }, - goroutines: []tracev2.GoID{7}, + goroutines: []trace.GoID{7}, }, } for id, summary := range summaries { @@ -184,7 +184,7 @@ func TestSummarizeTasksTrace(t *testing.T) { } // Check children. - gotChildren := make(map[tracev2.TaskID]struct{}) + gotChildren := make(map[trace.TaskID]struct{}) for _, child := range summary.Children { gotChildren[child.ID] = struct{}{} } @@ -236,7 +236,7 @@ func TestSummarizeTasksTrace(t *testing.T) { } } -func assertContainsGoroutine(t *testing.T, summaries map[tracev2.GoID]*GoroutineSummary, name string) { +func assertContainsGoroutine(t *testing.T, summaries map[trace.GoID]*trace.GoroutineSummary, name string) { for _, summary := range summaries { if summary.Name == name { return @@ -245,8 +245,8 @@ func assertContainsGoroutine(t *testing.T, summaries map[tracev2.GoID]*Goroutine t.Errorf("missing goroutine %s", name) } -func basicGoroutineSummaryChecks(t *testing.T, summary *GoroutineSummary) { - if summary.ID == tracev2.NoGoroutine { +func basicGoroutineSummaryChecks(t *testing.T, summary *trace.GoroutineSummary) { + if summary.ID == trace.NoGoroutine { t.Error("summary found for no goroutine") return } @@ -263,16 +263,16 @@ func basicGoroutineSummaryChecks(t *testing.T, summary *GoroutineSummary) { } } -func summarizeTraceTest(t *testing.T, testPath string) *Summary { - trace, _, err := testtrace.ParseFile(testPath) +func summarizeTraceTest(t *testing.T, testPath string) *trace.Summary { + trc, _, err := testtrace.ParseFile(testPath) if err != nil { t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } // Create the analysis state. - s := NewSummarizer() + s := trace.NewSummarizer() // Create a reader. - r, err := tracev2.NewReader(trace) + r, err := trace.NewReader(trc) if err != nil { t.Fatalf("failed to create trace reader for %s: %v", testPath, err) } @@ -290,13 +290,13 @@ func summarizeTraceTest(t *testing.T, testPath string) *Summary { return s.Finalize() } -func checkRegionEvents(t *testing.T, wantStart, wantEnd tracev2.EventKind, goid tracev2.GoID, region *UserRegionSummary) { +func checkRegionEvents(t *testing.T, wantStart, wantEnd trace.EventKind, goid trace.GoID, region *trace.UserRegionSummary) { switch wantStart { - case tracev2.EventBad: + case trace.EventBad: if region.Start != nil { t.Errorf("expected nil region start event, got\n%s", region.Start.String()) } - case tracev2.EventStateTransition, tracev2.EventRegionBegin: + case trace.EventStateTransition, trace.EventRegionBegin: if region.Start == nil { t.Error("expected non-nil region start event, got nil") } @@ -304,19 +304,19 @@ func checkRegionEvents(t *testing.T, wantStart, wantEnd tracev2.EventKind, goid if kind != wantStart { t.Errorf("wanted region start event %s, got %s", wantStart, kind) } - if kind == tracev2.EventRegionBegin { + if kind == trace.EventRegionBegin { if region.Start.Region().Type != region.Name { t.Errorf("region name mismatch: event has %s, summary has %s", region.Start.Region().Type, region.Name) } } else { st := region.Start.StateTransition() - if st.Resource.Kind != tracev2.ResourceGoroutine { + if st.Resource.Kind != trace.ResourceGoroutine { t.Errorf("found region start event for the wrong resource: %s", st.Resource) } if st.Resource.Goroutine() != goid { t.Errorf("found region start event for the wrong resource: wanted goroutine %d, got %s", goid, st.Resource) } - if old, _ := st.Goroutine(); old != tracev2.GoNotExist && old != tracev2.GoUndetermined { + if old, _ := st.Goroutine(); old != trace.GoNotExist && old != trace.GoUndetermined { t.Errorf("expected transition from GoNotExist or GoUndetermined, got transition from %s instead", old) } } @@ -325,11 +325,11 @@ func checkRegionEvents(t *testing.T, wantStart, wantEnd tracev2.EventKind, goid } switch wantEnd { - case tracev2.EventBad: + case trace.EventBad: if region.End != nil { t.Errorf("expected nil region end event, got\n%s", region.End.String()) } - case tracev2.EventStateTransition, tracev2.EventRegionEnd: + case trace.EventStateTransition, trace.EventRegionEnd: if region.End == nil { t.Error("expected non-nil region end event, got nil") } @@ -337,19 +337,19 @@ func checkRegionEvents(t *testing.T, wantStart, wantEnd tracev2.EventKind, goid if kind != wantEnd { t.Errorf("wanted region end event %s, got %s", wantEnd, kind) } - if kind == tracev2.EventRegionEnd { + if kind == trace.EventRegionEnd { if region.End.Region().Type != region.Name { t.Errorf("region name mismatch: event has %s, summary has %s", region.End.Region().Type, region.Name) } } else { st := region.End.StateTransition() - if st.Resource.Kind != tracev2.ResourceGoroutine { + if st.Resource.Kind != trace.ResourceGoroutine { t.Errorf("found region end event for the wrong resource: %s", st.Resource) } if st.Resource.Goroutine() != goid { t.Errorf("found region end event for the wrong resource: wanted goroutine %d, got %s", goid, st.Resource) } - if _, new := st.Goroutine(); new != tracev2.GoNotExist { + if _, new := st.Goroutine(); new != trace.GoNotExist { t.Errorf("expected transition to GoNotExist, got transition to %s instead", new) } } @@ -358,7 +358,7 @@ func checkRegionEvents(t *testing.T, wantStart, wantEnd tracev2.EventKind, goid } } -func basicGoroutineExecStatsChecks(t *testing.T, stats *GoroutineExecStats) { +func basicGoroutineExecStatsChecks(t *testing.T, stats *trace.GoroutineExecStats) { if stats.ExecTime < 0 { t.Error("found negative ExecTime") } @@ -387,20 +387,20 @@ func basicGoroutineExecStatsChecks(t *testing.T, stats *GoroutineExecStats) { } func TestRelatedGoroutinesV2Trace(t *testing.T) { - testPath := "v2/testdata/tests/go122-gc-stress.test" - trace, _, err := testtrace.ParseFile(testPath) + testPath := "testdata/tests/go122-gc-stress.test" + trc, _, err := testtrace.ParseFile(testPath) if err != nil { t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } // Create a reader. - r, err := tracev2.NewReader(trace) + r, err := trace.NewReader(trc) if err != nil { t.Fatalf("failed to create trace reader for %s: %v", testPath, err) } // Collect all the events. - var events []tracev2.Event + var events []trace.Event for { ev, err := r.ReadEvent() if err == io.EOF { @@ -413,13 +413,13 @@ func TestRelatedGoroutinesV2Trace(t *testing.T) { } // Test the function. - targetg := tracev2.GoID(86) - got := RelatedGoroutinesV2(events, targetg) - want := map[tracev2.GoID]struct{}{ - tracev2.GoID(86): struct{}{}, // N.B. Result includes target. - tracev2.GoID(71): struct{}{}, - tracev2.GoID(25): struct{}{}, - tracev2.GoID(122): struct{}{}, + targetg := trace.GoID(86) + got := trace.RelatedGoroutinesV2(events, targetg) + want := map[trace.GoID]struct{}{ + trace.GoID(86): struct{}{}, // N.B. Result includes target. + trace.GoID(71): struct{}{}, + trace.GoID(25): struct{}{}, + trace.GoID(122): struct{}{}, } for goid := range got { if _, ok := want[goid]; ok { diff --git a/src/internal/trace/testdata/README.md b/src/internal/trace/testdata/README.md new file mode 100644 index 0000000000..0fae9caf37 --- /dev/null +++ b/src/internal/trace/testdata/README.md @@ -0,0 +1,38 @@ +# Trace test data + +## Trace golden tests + +Trace tests can be generated by running + +``` +go generate . +``` + +with the relevant toolchain in this directory. + +This will put the tests into a `tests` directory where the trace reader +tests will find them. + +A subset of tests can be regenerated by specifying a regexp pattern for +the names of tests to generate in the `GOTRACETEST` environment +variable. +Test names are defined as the name of the `.go` file that generates the +trace, but with the `.go` extension removed. + +## Trace test programs + +The trace test programs in the `testprog` directory generate traces to +stdout. +Otherwise they're just normal programs. + +## Trace debug commands + +The `cmd` directory contains helpful tools for debugging traces. + +* `gotraceraw` parses traces without validation. + It can produce a text version of the trace wire format, or convert + the text format back into bytes. +* `gotracevalidate` parses traces and validates them. + It performs more rigorous checks than the parser does on its own, + which helps for debugging the parser as well. + In fact, it performs the exact same checks that the tests do. diff --git a/src/internal/trace/testdata/cmd/cmd/gotraceeventstats/main.go b/src/internal/trace/testdata/cmd/cmd/gotraceeventstats/main.go new file mode 100644 index 0000000000..ad06af6481 --- /dev/null +++ b/src/internal/trace/testdata/cmd/cmd/gotraceeventstats/main.go @@ -0,0 +1,136 @@ +// 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. + +package main + +import ( + "cmp" + "encoding/binary" + "flag" + "fmt" + "io" + "log" + "os" + "slices" + "text/tabwriter" + + "internal/trace/v2/event" + "internal/trace/v2/raw" +) + +func init() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0]) + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin.\n") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "* size - dumps size stats\n") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + flag.PrintDefaults() + } + log.SetFlags(0) +} + +func main() { + log.SetPrefix("") + flag.Parse() + + if flag.NArg() != 1 { + log.Print("missing mode argument") + flag.Usage() + os.Exit(1) + } + var err error + switch mode := flag.Arg(0); mode { + case "size": + err = printSizeStats(os.Stdin) + default: + log.Printf("unknown mode %q", mode) + flag.Usage() + os.Exit(1) + } + if err != nil { + log.Fatalf("error: %v", err) + os.Exit(1) + } +} + +func printSizeStats(r io.Reader) error { + cr := countingReader{Reader: r} + tr, err := raw.NewReader(&cr) + if err != nil { + return err + } + type eventStats struct { + typ event.Type + count int + bytes int + } + var stats [256]eventStats + for i := range stats { + stats[i].typ = event.Type(i) + } + eventsRead := 0 + for { + e, err := tr.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + return err + } + s := &stats[e.Ev] + s.count++ + s.bytes += encodedSize(&e) + eventsRead++ + } + slices.SortFunc(stats[:], func(a, b eventStats) int { + return cmp.Compare(b.bytes, a.bytes) + }) + specs := tr.Version().Specs() + w := tabwriter.NewWriter(os.Stdout, 3, 8, 2, ' ', 0) + fmt.Fprintf(w, "Event\tBytes\t%%\tCount\t%%\n") + fmt.Fprintf(w, "-\t-\t-\t-\t-\n") + for i := range stats { + stat := &stats[i] + name := "" + if int(stat.typ) >= len(specs) { + name = fmt.Sprintf("", stat.typ) + } else { + name = specs[stat.typ].Name + } + bytesPct := float64(stat.bytes) / float64(cr.bytesRead) * 100 + countPct := float64(stat.count) / float64(eventsRead) * 100 + fmt.Fprintf(w, "%s\t%d\t%.2f%%\t%d\t%.2f%%\n", name, stat.bytes, bytesPct, stat.count, countPct) + } + w.Flush() + return nil +} + +func encodedSize(e *raw.Event) int { + size := 1 + var buf [binary.MaxVarintLen64]byte + for _, arg := range e.Args { + size += binary.PutUvarint(buf[:], arg) + } + spec := e.Version.Specs()[e.Ev] + if spec.HasData { + size += binary.PutUvarint(buf[:], uint64(len(e.Data))) + size += len(e.Data) + } + return size +} + +type countingReader struct { + io.Reader + bytesRead int +} + +func (r *countingReader) Read(b []byte) (int, error) { + n, err := r.Reader.Read(b) + r.bytesRead += n + return n, err +} diff --git a/src/internal/trace/testdata/cmd/gotraceraw/main.go b/src/internal/trace/testdata/cmd/gotraceraw/main.go new file mode 100644 index 0000000000..3df11be7a8 --- /dev/null +++ b/src/internal/trace/testdata/cmd/gotraceraw/main.go @@ -0,0 +1,86 @@ +// 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. + +package main + +import ( + "flag" + "fmt" + "io" + "log" + "os" + + "internal/trace/v2/raw" + "internal/trace/v2/version" +) + +func init() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0]) + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "* text2bytes - converts a text format trace to bytes\n") + fmt.Fprintf(flag.CommandLine.Output(), "* bytes2text - converts a byte format trace to text\n") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + flag.PrintDefaults() + } + log.SetFlags(0) +} + +func main() { + flag.Parse() + if narg := flag.NArg(); narg != 1 { + log.Fatal("expected exactly one positional argument: the mode to operate in; see -h output") + } + + r := os.Stdin + w := os.Stdout + + var tr traceReader + var tw traceWriter + var err error + + switch flag.Arg(0) { + case "text2bytes": + tr, err = raw.NewTextReader(r) + if err != nil { + log.Fatal(err) + } + tw, err = raw.NewWriter(w, tr.Version()) + if err != nil { + log.Fatal(err) + } + case "bytes2text": + tr, err = raw.NewReader(r) + if err != nil { + log.Fatal(err) + } + tw, err = raw.NewTextWriter(w, tr.Version()) + if err != nil { + log.Fatal(err) + } + } + for { + ev, err := tr.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + log.Fatal(err) + } + if err := tw.WriteEvent(ev); err != nil { + log.Fatal(err) + } + } +} + +type traceReader interface { + Version() version.Version + ReadEvent() (raw.Event, error) +} + +type traceWriter interface { + WriteEvent(raw.Event) error +} diff --git a/src/internal/trace/testdata/cmd/gotracevalidate/main.go b/src/internal/trace/testdata/cmd/gotracevalidate/main.go new file mode 100644 index 0000000000..944d19f85e --- /dev/null +++ b/src/internal/trace/testdata/cmd/gotracevalidate/main.go @@ -0,0 +1,53 @@ +// 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. + +package main + +import ( + "flag" + "fmt" + "io" + "log" + "os" + + "internal/trace/v2" + "internal/trace/v2/testtrace" +) + +func init() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s\n", os.Args[0]) + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin and validates it.\n") + flag.PrintDefaults() + } + log.SetFlags(0) +} + +var logEvents = flag.Bool("log-events", false, "whether to log events") + +func main() { + flag.Parse() + + r, err := trace.NewReader(os.Stdin) + if err != nil { + log.Fatal(err) + } + v := testtrace.NewValidator() + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + log.Fatal(err) + } + if *logEvents { + log.Println(ev.String()) + } + if err := v.Event(ev); err != nil { + log.Fatal(err) + } + } +} diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/0cb1786dee0f090b b/src/internal/trace/testdata/fuzz/FuzzReader/0cb1786dee0f090b new file mode 100644 index 0000000000..326ebe1c6e --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/0cb1786dee0f090b @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x190000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/1e45307d5b2ec36d b/src/internal/trace/testdata/fuzz/FuzzReader/1e45307d5b2ec36d new file mode 100644 index 0000000000..406af9caa6 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/1e45307d5b2ec36d @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0001") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/2b05796f9b2fc48d b/src/internal/trace/testdata/fuzz/FuzzReader/2b05796f9b2fc48d new file mode 100644 index 0000000000..50fdccda6b --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/2b05796f9b2fc48d @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00-0000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/2b9be9aebe08d511 b/src/internal/trace/testdata/fuzz/FuzzReader/2b9be9aebe08d511 new file mode 100644 index 0000000000..6bcb99adfc --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/2b9be9aebe08d511 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x0f00\x120\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/344331b314da0b08 b/src/internal/trace/testdata/fuzz/FuzzReader/344331b314da0b08 new file mode 100644 index 0000000000..de6e4694be --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/344331b314da0b08 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\b0000\x01\x01\xff00\xb8\x00\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1901\xff\xff\xff\xff\xff\xff\xff\xff0\x800") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/365d7b5b633b3f97 b/src/internal/trace/testdata/fuzz/FuzzReader/365d7b5b633b3f97 new file mode 100644 index 0000000000..8dc370f383 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/365d7b5b633b3f97 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x0100\x8c0\x85\x00\b0000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/4055b17cae1a3443 b/src/internal/trace/testdata/fuzz/FuzzReader/4055b17cae1a3443 new file mode 100644 index 0000000000..ea5417c78a --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/4055b17cae1a3443 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.11 trace\x00\x00\x00A00\x020$0") diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/4d9ddc909984e871 b/src/internal/trace/testdata/fuzz/FuzzReader/4d9ddc909984e871 new file mode 100644 index 0000000000..040b2a4cae --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/4d9ddc909984e871 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x11\r\xa700\x01\x19000\x02$000000\x01\x0100\x05\b0000\x01\x0110\x11\r\xa700\x01\x19 00\x02\x110 0000") diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/56f073e57903588c b/src/internal/trace/testdata/fuzz/FuzzReader/56f073e57903588c new file mode 100644 index 0000000000..d34fe3f06c --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/56f073e57903588c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x1f0000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 b/src/internal/trace/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 new file mode 100644 index 0000000000..5677261155 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x11\r\xa700\x01\x19000\x02#000000\x01\x0100\x05\b0000\x01\x0110\x11\r\xa700\x01\x19 00\x02\x110 0000") diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/aeb749b6bc317b66 b/src/internal/trace/testdata/fuzz/FuzzReader/aeb749b6bc317b66 new file mode 100644 index 0000000000..f93b5a90da --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/aeb749b6bc317b66 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/closing-unknown-region b/src/internal/trace/testdata/fuzz/FuzzReader/closing-unknown-region new file mode 100644 index 0000000000..7433214030 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/closing-unknown-region @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xf6\x9f\n\x9fÕ\xb4\x99\xb2\x06\x11\r\xa7\x02\x00\x01\x19\x05\x01\xf6\x9f\n\x02+\x04\x01\x00\x00") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/d478e18d2d6756b7 b/src/internal/trace/testdata/fuzz/FuzzReader/d478e18d2d6756b7 new file mode 100644 index 0000000000..3e5fda833a --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/d478e18d2d6756b7 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\"0000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/d91203cd397aa0bc b/src/internal/trace/testdata/fuzz/FuzzReader/d91203cd397aa0bc new file mode 100644 index 0000000000..d24b94ac97 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/d91203cd397aa0bc @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01001\x85\x00\b0000") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/invalid-proc-state b/src/internal/trace/testdata/fuzz/FuzzReader/invalid-proc-state new file mode 100644 index 0000000000..e5d3258111 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/invalid-proc-state @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\x05\r\xa7\x02\x00E") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/large-id b/src/internal/trace/testdata/fuzz/FuzzReader/large-id new file mode 100644 index 0000000000..0fb6273b44 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/large-id @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\f\x02\x03\xff\xff\xff\xff\xff\xff\xff\x9f\x1d\x00") \ No newline at end of file diff --git a/src/internal/trace/testdata/fuzz/FuzzReader/malformed-timestamp b/src/internal/trace/testdata/fuzz/FuzzReader/malformed-timestamp new file mode 100644 index 0000000000..850ca50f87 --- /dev/null +++ b/src/internal/trace/testdata/fuzz/FuzzReader/malformed-timestamp @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xfa\x9f\n\xa5ѕ\xb4\x99\xb2\x06\x0e\n\x97\x96\x96\x96\x96\x96\x96\x96\x96\x96\x01\x01\x01") diff --git a/src/internal/trace/testdata/generate.go b/src/internal/trace/testdata/generate.go new file mode 100644 index 0000000000..c0658b2329 --- /dev/null +++ b/src/internal/trace/testdata/generate.go @@ -0,0 +1,6 @@ +// 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. + +//go:generate go run mktests.go +package testdata diff --git a/src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go b/src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go new file mode 100644 index 0000000000..f618c41e78 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go @@ -0,0 +1,62 @@ +// 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. + +// Regression test for an issue found in development. +// +// The core of the issue is that if generation counters +// aren't considered as part of sequence numbers, then +// it's possible to accidentally advance without a +// GoStatus event. +// +// The situation is one in which it just so happens that +// an event on the frontier for a following generation +// has a sequence number exactly one higher than the last +// sequence number for e.g. a goroutine in the previous +// generation. The parser should wait to find a GoStatus +// event before advancing into the next generation at all. +// It turns out this situation is pretty rare; the GoStatus +// event almost always shows up first in practice. But it +// can and did happen. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g1 := t.Generation(1) + + // A running goroutine blocks. + b10 := g1.Batch(trace.ThreadID(0), 0) + b10.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b10.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b10.Event("GoStop", "whatever", testgen.NoStack) + + // The running goroutine gets unblocked. + b11 := g1.Batch(trace.ThreadID(1), 0) + b11.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) + b11.Event("GoStart", trace.GoID(1), testgen.Seq(1)) + b11.Event("GoStop", "whatever", testgen.NoStack) + + g2 := t.Generation(2) + + // Start running the goroutine, but later. + b21 := g2.Batch(trace.ThreadID(1), 3) + b21.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) + b21.Event("GoStart", trace.GoID(1), testgen.Seq(2)) + + // The goroutine starts running, then stops, then starts again. + b20 := g2.Batch(trace.ThreadID(0), 5) + b20.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b20.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunnable) + b20.Event("GoStart", trace.GoID(1), testgen.Seq(1)) + b20.Event("GoStop", "whatever", testgen.NoStack) +} diff --git a/src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go b/src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go new file mode 100644 index 0000000000..107cce2cc2 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go @@ -0,0 +1,61 @@ +// 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 G being created from within a syscall. +// +// Specifically, it tests a scenerio wherein a C +// thread is calling into Go, creating a goroutine in +// a syscall (in the tracer's model). The system is free +// to reuse thread IDs, so first a thread ID is used to +// call into Go, and then is used for a Go-created thread. +// +// This is a regression test. The trace parser didn't correctly +// model GoDestroySyscall as dropping its P (even if the runtime +// did). It turns out this is actually fine if all the threads +// in the trace have unique IDs, since the P just stays associated +// with an eternally dead thread, and it's stolen by some other +// thread later. But if thread IDs are reused, then the tracer +// gets confused when trying to advance events on the new thread. +// The now-dead thread which exited on a GoDestroySyscall still has +// its P associated and this transfers to the newly-live thread +// in the parser's state because they share a thread ID. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // A C thread calls into Go and acquires a P. It returns + // back to C, destroying the G. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("GoCreateSyscall", trace.GoID(4)) + b0.Event("GoSyscallEndBlocked") + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) + b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) + b0.Event("GoStatus", trace.GoID(4), trace.NoThread, go122.GoRunnable) + b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) + b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) + b0.Event("GoDestroySyscall") + + // A new Go-created thread with the same ID appears and + // starts running, then tries to steal the P from the + // first thread. The stealing is interesting because if + // the parser handles GoDestroySyscall wrong, then we + // have a self-steal here potentially that doesn't make + // sense. + b1 := g.Batch(trace.ThreadID(0), 0) + b1.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b1.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(3), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-create-syscall-with-p.go b/src/internal/trace/testdata/generators/go122-create-syscall-with-p.go new file mode 100644 index 0000000000..4cb1c4a9a7 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-create-syscall-with-p.go @@ -0,0 +1,52 @@ +// 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 G being created from within a syscall. +// +// Specifically, it tests a scenerio wherein a C +// thread is calling into Go, creating a goroutine in +// a syscall (in the tracer's model). Because the actual +// m can be reused, it's possible for that m to have never +// had its P (in _Psyscall) stolen if the runtime doesn't +// model the scenario correctly. Make sure we reject such +// traces. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + t.ExpectFailure(".*expected a proc but didn't have one.*") + + g := t.Generation(1) + + // A C thread calls into Go and acquires a P. It returns + // back to C, destroying the G. It then comes back to Go + // on the same thread and again returns to C. + // + // Note: on pthread platforms this can't happen on the + // same thread because the m is stashed in TLS between + // calls into Go, until the thread dies. This is still + // possible on other platforms, however. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("GoCreateSyscall", trace.GoID(4)) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) + b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) + b0.Event("GoSyscallEndBlocked") + b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) + b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) + b0.Event("GoDestroySyscall") + b0.Event("GoCreateSyscall", trace.GoID(4)) + b0.Event("GoSyscallEnd") + b0.Event("GoSyscallBegin", testgen.Seq(3), testgen.NoStack) + b0.Event("GoDestroySyscall") +} diff --git a/src/internal/trace/testdata/generators/go122-fail-first-gen-first.go b/src/internal/trace/testdata/generators/go122-fail-first-gen-first.go new file mode 100644 index 0000000000..e5dea24e3b --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-fail-first-gen-first.go @@ -0,0 +1,44 @@ +// 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. + +// Regression test for #55160. +// +// The issue is that the parser reads ahead to the first batch of the +// next generation to find generation boundaries, but if it finds an +// error, it needs to delay handling that error until later. Previously +// it would handle that error immediately and a totally valid generation +// would be skipped for parsing and rejected because of an error in a +// batch in the following generation. +// +// This test captures this behavior by making both the first generation +// and second generation bad. It requires that the issue in the first +// generation, which is caught when actually ordering events, be reported +// instead of the second one. + +package main + +import ( + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + // A running goroutine emits a task begin. + t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) + t.RawEvent(go122.EvFrequency, nil, 15625000) + + // A running goroutine emits a task begin. + t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) + t.RawEvent(go122.EvGoCreate, nil, 0 /*timestamp delta*/, 1 /*go ID*/, 0, 0) + + // Write an invalid batch event for the next generation. + t.RawEvent(go122.EvEventBatch, nil, 2 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 50 /*batch length (invalid)*/) + + // We should fail at the first issue, not the second one. + t.ExpectFailure("expected a proc but didn't have one") +} diff --git a/src/internal/trace/testdata/generators/go122-go-create-without-running-g.go b/src/internal/trace/testdata/generators/go122-go-create-without-running-g.go new file mode 100644 index 0000000000..b693245b5b --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-go-create-without-running-g.go @@ -0,0 +1,33 @@ +// 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. + +// Regression test for an issue found in development. +// +// GoCreate events can happen on bare Ps in a variety of situations and +// and earlier version of the parser assumed this wasn't possible. At +// the time of writing, one such example is goroutines created by expiring +// timers. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g1 := t.Generation(1) + + // A goroutine gets created on a running P, then starts running. + b0 := g1.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoCreate", trace.GoID(5), testgen.NoStack, testgen.NoStack) + b0.Event("GoStart", trace.GoID(5), testgen.Seq(1)) + b0.Event("GoStop", "whatever", testgen.NoStack) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go new file mode 100644 index 0000000000..349a575ef3 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.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 syscall P stealing. +// +// Specifically, it tests a scenerio wherein, without a +// P sequence number of GoSyscallBegin, the syscall that +// a ProcSteal applies to is ambiguous. This only happens in +// practice when the events aren't already properly ordered +// by timestamp, since the ProcSteal won't be seen until after +// the correct GoSyscallBegin appears on the frontier. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + t.DisableTimestamps() + + g := t.Generation(1) + + // One goroutine does a syscall without blocking, then another one where + // it's P gets stolen. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) + b0.Event("GoSyscallEnd") + b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) + b0.Event("GoSyscallEndBlocked") + + // A running goroutine steals proc 0. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(3), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go new file mode 100644 index 0000000000..f4c9f6ecf3 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go @@ -0,0 +1,33 @@ +// 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 syscall P stealing at a generation boundary. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine is exiting with a syscall. It already + // acquired a new P. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) + b0.Event("GoSyscallEndBlocked") + + // A bare M stole the goroutine's P at the generation boundary. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go new file mode 100644 index 0000000000..e6023ba701 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go @@ -0,0 +1,34 @@ +// 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 syscall P stealing at a generation boundary. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine is exiting with a syscall. It already + // acquired a new P. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) + b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) + b0.Event("GoSyscallEndBlocked") + + // A bare M stole the goroutine's P at the generation boundary. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go new file mode 100644 index 0000000000..2232dca5dc --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go @@ -0,0 +1,36 @@ +// 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 syscall P stealing at a generation boundary. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine is exiting with a syscall. It already + // acquired a new P. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) + b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) + b0.Event("GoSyscallEndBlocked") + + // A running goroutine stole P0 at the generation boundary. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go new file mode 100644 index 0000000000..710827a8f6 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go @@ -0,0 +1,35 @@ +// 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 syscall P stealing at a generation boundary. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine is exiting with a syscall. It already + // acquired a new P. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) + b0.Event("GoSyscallEndBlocked") + + // A running goroutine stole P0 at the generation boundary. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go new file mode 100644 index 0000000000..24e5cb2a3e --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go @@ -0,0 +1,34 @@ +// 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 syscall P stealing. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine enters a syscall, grabs a P, and starts running. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) + b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) + b0.Event("GoSyscallEndBlocked") + + // A bare M steals the goroutine's P. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go new file mode 100644 index 0000000000..2caefe8be5 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go @@ -0,0 +1,36 @@ +// 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 syscall P stealing. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine enters a syscall, grabs a P, and starts running. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) + b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) + b0.Event("GoSyscallEndBlocked") + + // A running goroutine steals proc 0. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go new file mode 100644 index 0000000000..dd947346c6 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go @@ -0,0 +1,37 @@ +// 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 syscall P stealing. +// +// Specifically, it tests a scenario where a thread 'steals' +// a P from itself. It's just a ProcStop with extra steps when +// it happens on the same P. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + t.DisableTimestamps() + + g := t.Generation(1) + + // A goroutine execute a syscall and steals its own P, then starts running + // on that P. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) + b0.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) + b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(3)) + b0.Event("GoSyscallEndBlocked") +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go new file mode 100644 index 0000000000..630eba8cf2 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go @@ -0,0 +1,32 @@ +// 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 syscall P stealing. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine enters a syscall. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) + b0.Event("GoSyscallEndBlocked") + + // A bare M steals the goroutine's P. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go new file mode 100644 index 0000000000..54b43f4f0b --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go @@ -0,0 +1,34 @@ +// 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 syscall P stealing. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // One goroutine enters a syscall. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) + b0.Event("GoSyscallEndBlocked") + + // A running goroutine steals proc 0. + b1 := g.Batch(trace.ThreadID(1), 0) + b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) +} diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go new file mode 100644 index 0000000000..870f8f69f6 --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go @@ -0,0 +1,32 @@ +// 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 syscall P stealing from a goroutine and thread +// that have been in a syscall the entire generation. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // Steal proc from a goroutine that's been blocked + // in a syscall the entire generation. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b0.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(1)) + + // Status event for a goroutine blocked in a syscall for the entire generation. + bz := g.Batch(trace.NoThread, 0) + bz.Event("GoStatus", trace.GoID(1), trace.ThreadID(1), go122.GoSyscall) +} diff --git a/src/internal/trace/testdata/generators/go122-task-across-generations.go b/src/internal/trace/testdata/generators/go122-task-across-generations.go new file mode 100644 index 0000000000..06ef96e51a --- /dev/null +++ b/src/internal/trace/testdata/generators/go122-task-across-generations.go @@ -0,0 +1,41 @@ +// 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. + +// Regression test for an issue found in development. +// +// The issue is that EvUserTaskEnd events don't carry the +// task type with them, so the parser needs to track that +// information. But if the parser just tracks the string ID +// and not the string itself, that string ID may not be valid +// for use in future generations. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g1 := t.Generation(1) + + // A running goroutine emits a task begin. + b1 := g1.Batch(trace.ThreadID(0), 0) + b1.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b1.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b1.Event("UserTaskBegin", trace.TaskID(2), trace.TaskID(0) /* 0 means no parent, not background */, "my task", testgen.NoStack) + + g2 := t.Generation(2) + + // That same goroutine emits a task end in the following generation. + b2 := g2.Batch(trace.ThreadID(0), 5) + b2.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b2.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b2.Event("UserTaskEnd", trace.TaskID(2), testgen.NoStack) +} diff --git a/src/internal/trace/testdata/mktests.go b/src/internal/trace/testdata/mktests.go new file mode 100644 index 0000000000..d8d2e9065d --- /dev/null +++ b/src/internal/trace/testdata/mktests.go @@ -0,0 +1,160 @@ +// 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. + +//go:build ignore + +package main + +import ( + "bytes" + "fmt" + "internal/trace/raw" + "internal/trace/version" + "internal/txtar" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" +) + +func main() { + log.SetFlags(0) + ctx, err := newContext() + if err != nil { + log.Fatal(err) + } + if err := ctx.runGenerators(); err != nil { + log.Fatal(err) + } + if err := ctx.runTestProg("./testprog/annotations.go"); err != nil { + log.Fatal(err) + } + if err := ctx.runTestProg("./testprog/annotations-stress.go"); err != nil { + log.Fatal(err) + } +} + +type context struct { + testNames map[string]struct{} + filter *regexp.Regexp +} + +func newContext() (*context, error) { + var filter *regexp.Regexp + var err error + if pattern := os.Getenv("GOTRACETEST"); pattern != "" { + filter, err = regexp.Compile(pattern) + if err != nil { + return nil, fmt.Errorf("compiling regexp %q for GOTRACETEST: %v", pattern, err) + } + } + return &context{ + testNames: make(map[string]struct{}), + filter: filter, + }, nil +} + +func (ctx *context) register(testName string) (skip bool, err error) { + if _, ok := ctx.testNames[testName]; ok { + return true, fmt.Errorf("duplicate test %s found", testName) + } + if ctx.filter != nil { + return !ctx.filter.MatchString(testName), nil + } + return false, nil +} + +func (ctx *context) runGenerators() error { + generators, err := filepath.Glob("./generators/*.go") + if err != nil { + return fmt.Errorf("reading generators: %v", err) + } + genroot := "./tests" + + if err := os.MkdirAll(genroot, 0777); err != nil { + return fmt.Errorf("creating generated root: %v", err) + } + for _, path := range generators { + name := filepath.Base(path) + name = name[:len(name)-len(filepath.Ext(name))] + + // Skip if we have a pattern and this test doesn't match. + skip, err := ctx.register(name) + if err != nil { + return err + } + if skip { + continue + } + + fmt.Fprintf(os.Stderr, "generating %s... ", name) + + // Get the test path. + testPath := filepath.Join(genroot, fmt.Sprintf("%s.test", name)) + + // Run generator. + cmd := exec.Command("go", "run", path, testPath) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("running generator %s: %v:\n%s", name, err, out) + } + fmt.Fprintln(os.Stderr) + } + return nil +} + +func (ctx *context) runTestProg(progPath string) error { + name := filepath.Base(progPath) + name = name[:len(name)-len(filepath.Ext(name))] + name = fmt.Sprintf("go1%d-%s", version.Current, name) + + // Skip if we have a pattern and this test doesn't match. + skip, err := ctx.register(name) + if err != nil { + return err + } + if skip { + return nil + } + + // Create command. + var trace, stderr bytes.Buffer + cmd := exec.Command("go", "run", progPath) + cmd.Stdout = &trace + cmd.Stderr = &stderr + + // Run trace program; the trace will appear in stdout. + fmt.Fprintf(os.Stderr, "running trace program %s...\n", name) + if err := cmd.Run(); err != nil { + log.Fatalf("running trace program: %v:\n%s", err, stderr.String()) + } + + // Write out the trace. + var textTrace bytes.Buffer + r, err := raw.NewReader(&trace) + if err != nil { + log.Fatalf("reading trace: %v", err) + } + w, err := raw.NewTextWriter(&textTrace, version.Current) + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + log.Fatalf("reading trace: %v", err) + } + if err := w.WriteEvent(ev); err != nil { + log.Fatalf("writing trace: %v", err) + } + } + testData := txtar.Format(&txtar.Archive{ + Files: []txtar.File{ + {Name: "expect", Data: []byte("SUCCESS")}, + {Name: "trace", Data: textTrace.Bytes()}, + }, + }) + return os.WriteFile(fmt.Sprintf("./tests/%s.test", name), testData, 0o664) +} diff --git a/src/internal/trace/testdata/testprog/annotations-stress.go b/src/internal/trace/testdata/testprog/annotations-stress.go new file mode 100644 index 0000000000..511d6ed890 --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/annotations.go b/src/internal/trace/testdata/testprog/annotations.go new file mode 100644 index 0000000000..2507bc4d38 --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/cgo-callback.go b/src/internal/trace/testdata/testprog/cgo-callback.go new file mode 100644 index 0000000000..d63650047e --- /dev/null +++ b/src/internal/trace/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 + +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/testdata/testprog/cpu-profile.go b/src/internal/trace/testdata/testprog/cpu-profile.go new file mode 100644 index 0000000000..293a2acac8 --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/futile-wakeup.go b/src/internal/trace/testdata/testprog/futile-wakeup.go new file mode 100644 index 0000000000..cc489818fc --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/gc-stress.go b/src/internal/trace/testdata/testprog/gc-stress.go new file mode 100644 index 0000000000..7979234c40 --- /dev/null +++ b/src/internal/trace/testdata/testprog/gc-stress.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 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" + "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]*[1024]*node +var sink [][]byte + +func main() { + for i := range trees { + trees[i] = makeTree(6) + } + ballast = new([16]*[1024]*node) + for i := range ballast { + ballast[i] = new([1024]*node) + for j := range ballast[i] { + ballast[i][j] = &node{ + data: [128]byte{1, 2, 3, 4}, + } + } + } + + procs := runtime.GOMAXPROCS(-1) + sink = make([][]byte, procs) + + for i := 0; i < procs; i++ { + i := i + go func() { + for { + sink[i] = make([]byte, 4<<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/testdata/testprog/gomaxprocs.go b/src/internal/trace/testdata/testprog/gomaxprocs.go new file mode 100644 index 0000000000..265120767d --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/iter-pull.go b/src/internal/trace/testdata/testprog/iter-pull.go new file mode 100644 index 0000000000..ba8f41365e --- /dev/null +++ b/src/internal/trace/testdata/testprog/iter-pull.go @@ -0,0 +1,85 @@ +// 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 coroutine switches. + +//go:build ignore + +package main + +import ( + "iter" + "log" + "os" + "runtime/trace" + "sync" +) + +func main() { + // Start tracing. + if err := trace.Start(os.Stdout); err != nil { + log.Fatalf("failed to start tracing: %v", err) + } + + // Try simple pull iteration. + i := pullRange(100) + for { + _, ok := i.next() + if !ok { + break + } + } + + // Try bouncing the pull iterator between two goroutines. + var wg sync.WaitGroup + var iterChans [2]chan intIter + wg.Add(2) + iterChans[0] = make(chan intIter) + iterChans[1] = make(chan intIter) + go func() { + defer wg.Done() + + iter := pullRange(100) + iterChans[1] <- iter + + for i := range iterChans[0] { + _, ok := i.next() + if !ok { + close(iterChans[1]) + break + } + iterChans[1] <- i + } + }() + go func() { + defer wg.Done() + + for i := range iterChans[1] { + _, ok := i.next() + if !ok { + close(iterChans[0]) + break + } + iterChans[0] <- i + } + }() + wg.Wait() + + // End of traced execution. + trace.Stop() +} + +func pullRange(n int) intIter { + next, stop := iter.Pull(func(yield func(v int) bool) { + for i := range n { + yield(i) + } + }) + return intIter{next: next, stop: stop} +} + +type intIter struct { + next func() (int, bool) + stop func() +} diff --git a/src/internal/trace/testdata/testprog/many-start-stop.go b/src/internal/trace/testdata/testprog/many-start-stop.go new file mode 100644 index 0000000000..2d5d0634f0 --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/stacks.go b/src/internal/trace/testdata/testprog/stacks.go new file mode 100644 index 0000000000..e64bc86844 --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/stress-start-stop.go b/src/internal/trace/testdata/testprog/stress-start-stop.go new file mode 100644 index 0000000000..72c1c59417 --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/stress.go b/src/internal/trace/testdata/testprog/stress.go new file mode 100644 index 0000000000..99696d1756 --- /dev/null +++ b/src/internal/trace/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/testdata/testprog/wait-on-pipe.go b/src/internal/trace/testdata/testprog/wait-on-pipe.go new file mode 100644 index 0000000000..912f5dd3bc --- /dev/null +++ b/src/internal/trace/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() +} diff --git a/src/internal/trace/testdata/tests/go122-annotations-stress.test b/src/internal/trace/testdata/tests/go122-annotations-stress.test new file mode 100644 index 0000000000..8da8c0f318 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-annotations-stress.test @@ -0,0 +1,1179 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=18446744073709551615 time=2753926854385 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=1986497 time=2753925247434 size=1430 +ProcStart dt=336 p=2 p_seq=1 +GoStart dt=191 g=19 g_seq=1 +HeapAlloc dt=389 heapalloc_value=1622016 +HeapAlloc dt=4453 heapalloc_value=1662976 +GoBlock dt=572 reason_string=12 stack=29 +ProcStop dt=26 +ProcStart dt=160734 p=2 p_seq=2 +ProcStop dt=21 +ProcStart dt=159292 p=0 p_seq=7 +GoStart dt=299 g=49 g_seq=1 +UserRegionBegin dt=183 task=8 name_string=33 stack=26 +UserLog dt=26 task=8 key_string=24 value_string=49 stack=27 +UserRegionEnd dt=8 task=8 name_string=33 stack=28 +GoDestroy dt=3 +GoStart dt=20 g=50 g_seq=1 +UserRegionBegin dt=40 task=8 name_string=35 stack=26 +UserLog dt=9 task=8 key_string=24 value_string=50 stack=27 +UserRegionEnd dt=2 task=8 name_string=35 stack=28 +GoDestroy dt=1 +ProcStop dt=18 +ProcStart dt=141801 p=4 p_seq=5 +ProcStop dt=18 +ProcStart dt=16860 p=4 p_seq=6 +GoUnblock dt=53 g=1 g_seq=5 stack=0 +GoUnblock dt=9 g=51 g_seq=3 stack=0 +GoStart dt=162 g=51 g_seq=4 +UserTaskEnd dt=35 task=9 stack=36 +UserRegionEnd dt=16 task=8 name_string=31 stack=28 +GoDestroy dt=2 +GoStart dt=20 g=1 g_seq=6 +UserTaskEnd dt=14 task=8 stack=54 +UserLog dt=26 task=3 key_string=24 value_string=51 stack=55 +UserTaskBegin dt=14 task=10 parent_task=3 name_string=26 stack=56 +UserLog dt=42 task=10 key_string=27 value_string=52 stack=57 +UserRegionBegin dt=12 task=10 name_string=29 stack=58 +GoCreate dt=36 new_g=35 new_stack=17 stack=59 +GoCreate dt=11 new_g=36 new_stack=17 stack=59 +GoCreate dt=18 new_g=37 new_stack=17 stack=59 +GoCreate dt=10 new_g=38 new_stack=17 stack=59 +GoCreate dt=6 new_g=39 new_stack=17 stack=59 +GoCreate dt=8 new_g=40 new_stack=17 stack=59 +UserRegionEnd dt=7 task=10 name_string=29 stack=60 +GoBlock dt=9 reason_string=19 stack=61 +GoStart dt=15 g=40 g_seq=1 +UserRegionBegin dt=110 task=10 name_string=53 stack=26 +UserLog dt=16 task=10 key_string=24 value_string=54 stack=27 +UserRegionEnd dt=2 task=10 name_string=53 stack=28 +GoDestroy dt=2 +GoStart dt=6 g=38 g_seq=1 +UserRegionBegin dt=31 task=10 name_string=30 stack=26 +UserLog dt=5 task=10 key_string=24 value_string=55 stack=27 +UserRegionEnd dt=2 task=10 name_string=30 stack=28 +GoDestroy dt=1 +GoStart dt=2 g=39 g_seq=1 +UserRegionBegin dt=23 task=10 name_string=56 stack=26 +UserLog dt=6 task=10 key_string=24 value_string=57 stack=27 +UserRegionEnd dt=1 task=10 name_string=56 stack=28 +GoDestroy dt=1 +GoStart dt=8 g=35 g_seq=1 +UserRegionBegin dt=17 task=10 name_string=33 stack=26 +UserLog dt=4 task=10 key_string=24 value_string=58 stack=27 +UserRegionEnd dt=2 task=10 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=3 g=36 g_seq=1 +UserRegionBegin dt=19 task=10 name_string=35 stack=26 +UserLog dt=4 task=10 key_string=24 value_string=59 stack=27 +UserRegionEnd dt=2 task=10 name_string=35 stack=28 +GoDestroy dt=1 +ProcStop dt=11 +ProcStart dt=142205 p=0 p_seq=9 +ProcStop dt=19 +ProcStart dt=16811 p=0 p_seq=10 +GoUnblock dt=26 g=1 g_seq=7 stack=0 +GoStart dt=201 g=1 g_seq=8 +UserTaskEnd dt=24 task=10 stack=62 +UserLog dt=18 task=4 key_string=24 value_string=63 stack=63 +UserTaskBegin dt=11 task=12 parent_task=4 name_string=26 stack=64 +UserLog dt=21 task=12 key_string=27 value_string=64 stack=65 +UserRegionBegin dt=7 task=12 name_string=29 stack=66 +GoCreate dt=33 new_g=5 new_stack=17 stack=67 +GoCreate dt=12 new_g=6 new_stack=17 stack=67 +GoCreate dt=9 new_g=7 new_stack=17 stack=67 +GoCreate dt=8 new_g=8 new_stack=17 stack=67 +GoCreate dt=19 new_g=9 new_stack=17 stack=67 +UserRegionEnd dt=14 task=12 name_string=29 stack=68 +GoBlock dt=11 reason_string=19 stack=69 +GoStart dt=13 g=9 g_seq=1 +UserRegionBegin dt=70 task=12 name_string=56 stack=26 +UserLog dt=11 task=12 key_string=24 value_string=65 stack=27 +UserRegionEnd dt=3 task=12 name_string=56 stack=28 +GoDestroy dt=2 +GoStart dt=7 g=5 g_seq=1 +UserRegionBegin dt=24 task=12 name_string=33 stack=26 +UserLog dt=5 task=12 key_string=24 value_string=66 stack=27 +UserRegionEnd dt=2 task=12 name_string=33 stack=28 +GoDestroy dt=2 +GoStart dt=8 g=6 g_seq=1 +UserRegionBegin dt=15 task=12 name_string=35 stack=26 +UserLog dt=7 task=12 key_string=24 value_string=67 stack=27 +UserRegionEnd dt=2 task=12 name_string=35 stack=28 +GoDestroy dt=1 +GoStart dt=2 g=7 g_seq=1 +UserRegionBegin dt=13 task=12 name_string=31 stack=26 +UserLog dt=5 task=12 key_string=24 value_string=68 stack=27 +UserLog dt=6 task=12 key_string=24 value_string=69 stack=30 +UserTaskBegin dt=5 task=13 parent_task=12 name_string=26 stack=31 +UserLog dt=7 task=13 key_string=27 value_string=70 stack=32 +UserRegionBegin dt=4 task=13 name_string=29 stack=33 +UserRegionEnd dt=6 task=13 name_string=29 stack=34 +GoBlock dt=18 reason_string=19 stack=35 +GoStart dt=12 g=8 g_seq=1 +UserRegionBegin dt=22 task=12 name_string=30 stack=26 +UserLog dt=5 task=12 key_string=24 value_string=71 stack=27 +UserRegionEnd dt=2 task=12 name_string=30 stack=28 +GoDestroy dt=1 +ProcStop dt=20 +ProcStart dt=141838 p=4 p_seq=8 +ProcStop dt=16 +ProcStart dt=17652 p=4 p_seq=9 +GoUnblock dt=48 g=1 g_seq=9 stack=0 +GoUnblock dt=8 g=7 g_seq=2 stack=0 +GoStart dt=271 g=7 g_seq=3 +UserTaskEnd dt=25 task=13 stack=36 +UserRegionEnd dt=15 task=12 name_string=31 stack=28 +GoDestroy dt=4 +GoStart dt=19 g=1 g_seq=10 +UserTaskEnd dt=19 task=12 stack=70 +UserLog dt=21 task=0 key_string=24 value_string=72 stack=13 +UserTaskBegin dt=19 task=14 parent_task=0 name_string=26 stack=14 +UserLog dt=37 task=14 key_string=27 value_string=73 stack=15 +UserRegionBegin dt=6 task=14 name_string=29 stack=16 +GoCreate dt=28 new_g=41 new_stack=17 stack=18 +GoCreate dt=14 new_g=42 new_stack=17 stack=18 +GoCreate dt=12 new_g=43 new_stack=17 stack=18 +GoCreate dt=10 new_g=44 new_stack=17 stack=18 +UserRegionEnd dt=5 task=14 name_string=29 stack=19 +GoBlock dt=9 reason_string=19 stack=20 +GoStart dt=16 g=44 g_seq=1 +UserRegionBegin dt=107 task=14 name_string=30 stack=26 +UserLog dt=16 task=14 key_string=24 value_string=74 stack=27 +UserRegionEnd dt=3 task=14 name_string=30 stack=28 +GoDestroy dt=2 +GoStart dt=7 g=41 g_seq=1 +UserRegionBegin dt=30 task=14 name_string=33 stack=26 +UserLog dt=7 task=14 key_string=24 value_string=75 stack=27 +UserRegionEnd dt=2 task=14 name_string=33 stack=28 +GoDestroy dt=2 +GoStart dt=7 g=42 g_seq=1 +UserRegionBegin dt=27 task=14 name_string=35 stack=26 +UserLog dt=7 task=14 key_string=24 value_string=76 stack=27 +UserRegionEnd dt=2 task=14 name_string=35 stack=28 +GoDestroy dt=2 +ProcStop dt=28 +ProcStart dt=141923 p=0 p_seq=12 +ProcStop dt=19 +ProcStart dt=16780 p=0 p_seq=13 +GoUnblock dt=22 g=43 g_seq=2 stack=0 +GoStart dt=162 g=43 g_seq=3 +UserTaskEnd dt=16 task=15 stack=36 +UserRegionEnd dt=12 task=14 name_string=31 stack=28 +GoDestroy dt=2 +ProcStop dt=8 +ProcStart dt=1532 p=2 p_seq=9 +ProcStop dt=12 +ProcStart dt=141906 p=4 p_seq=11 +ProcStop dt=16 +ProcStart dt=16784 p=4 p_seq=12 +GoUnblock dt=20 g=1 g_seq=13 stack=0 +GoStart dt=191 g=1 g_seq=14 +UserTaskEnd dt=15 task=16 stack=45 +UserLog dt=17 task=2 key_string=24 value_string=84 stack=46 +UserTaskBegin dt=8 task=17 parent_task=2 name_string=26 stack=47 +UserLog dt=20 task=17 key_string=27 value_string=85 stack=48 +UserRegionBegin dt=6 task=17 name_string=29 stack=49 +GoCreate dt=28 new_g=45 new_stack=17 stack=50 +GoCreate dt=9 new_g=46 new_stack=17 stack=50 +GoCreate dt=10 new_g=47 new_stack=17 stack=50 +UserRegionEnd dt=5 task=17 name_string=29 stack=51 +GoBlock dt=6 reason_string=19 stack=52 +GoStart dt=10 g=47 g_seq=1 +UserRegionBegin dt=69 task=17 name_string=31 stack=26 +UserLog dt=11 task=17 key_string=24 value_string=86 stack=27 +UserLog dt=7 task=17 key_string=24 value_string=87 stack=30 +UserTaskBegin dt=5 task=18 parent_task=17 name_string=26 stack=31 +UserLog dt=7 task=18 key_string=27 value_string=88 stack=32 +UserRegionBegin dt=5 task=18 name_string=29 stack=33 +UserRegionEnd dt=4 task=18 name_string=29 stack=34 +HeapAlloc dt=35 heapalloc_value=1818624 +GoBlock dt=14 reason_string=19 stack=35 +HeapAlloc dt=11 heapalloc_value=1826816 +GoStart dt=10 g=45 g_seq=1 +UserRegionBegin dt=29 task=17 name_string=33 stack=26 +UserLog dt=9 task=17 key_string=24 value_string=89 stack=27 +UserRegionEnd dt=3 task=17 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=5 g=46 g_seq=1 +UserRegionBegin dt=15 task=17 name_string=35 stack=26 +UserLog dt=8 task=17 key_string=24 value_string=90 stack=27 +UserRegionEnd dt=2 task=17 name_string=35 stack=28 +GoDestroy dt=1 +ProcStop dt=3 +ProcStart dt=141981 p=0 p_seq=16 +ProcStop dt=19 +ProcStart dt=17153 p=0 p_seq=17 +GoUnblock dt=44 g=1 g_seq=15 stack=0 +GoUnblock dt=11 g=47 g_seq=2 stack=0 +GoStart dt=215 g=47 g_seq=3 +UserTaskEnd dt=22 task=18 stack=36 +UserRegionEnd dt=9 task=17 name_string=31 stack=28 +GoDestroy dt=3 +GoStart dt=19 g=1 g_seq=16 +UserTaskEnd dt=13 task=17 stack=54 +UserLog dt=18 task=3 key_string=24 value_string=91 stack=55 +UserTaskBegin dt=7 task=19 parent_task=3 name_string=26 stack=56 +UserLog dt=27 task=19 key_string=27 value_string=92 stack=57 +UserRegionBegin dt=8 task=19 name_string=29 stack=58 +GoCreate dt=30 new_g=10 new_stack=17 stack=59 +GoCreate dt=9 new_g=11 new_stack=17 stack=59 +GoCreate dt=11 new_g=12 new_stack=17 stack=59 +GoCreate dt=7 new_g=13 new_stack=17 stack=59 +GoCreate dt=7 new_g=14 new_stack=17 stack=59 +GoCreate dt=9 new_g=15 new_stack=17 stack=59 +UserRegionEnd dt=5 task=19 name_string=29 stack=60 +GoBlock dt=7 reason_string=19 stack=61 +GoStart dt=17 g=15 g_seq=1 +UserRegionBegin dt=61 task=19 name_string=53 stack=26 +UserLog dt=10 task=19 key_string=24 value_string=93 stack=27 +UserRegionEnd dt=3 task=19 name_string=53 stack=28 +GoDestroy dt=1 +GoStart dt=4 g=10 g_seq=1 +UserRegionBegin dt=26 task=19 name_string=33 stack=26 +UserLog dt=7 task=19 key_string=24 value_string=94 stack=27 +UserRegionEnd dt=2 task=19 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=4 g=11 g_seq=1 +UserRegionBegin dt=20 task=19 name_string=35 stack=26 +UserLog dt=5 task=19 key_string=24 value_string=95 stack=27 +UserRegionEnd dt=2 task=19 name_string=35 stack=28 +GoDestroy dt=1 +GoStart dt=7 g=12 g_seq=1 +UserRegionBegin dt=14 task=19 name_string=31 stack=26 +UserLog dt=4 task=19 key_string=24 value_string=96 stack=27 +UserLog dt=4 task=19 key_string=24 value_string=97 stack=30 +UserTaskBegin dt=7 task=20 parent_task=19 name_string=26 stack=31 +UserLog dt=5 task=20 key_string=27 value_string=98 stack=32 +UserRegionBegin dt=4 task=20 name_string=29 stack=33 +UserRegionEnd dt=5 task=20 name_string=29 stack=34 +GoBlock dt=9 reason_string=19 stack=35 +GoStart dt=9 g=14 g_seq=1 +UserRegionBegin dt=28 task=19 name_string=56 stack=26 +UserLog dt=7 task=19 key_string=24 value_string=99 stack=27 +UserRegionEnd dt=2 task=19 name_string=56 stack=28 +GoDestroy dt=2 +ProcStop dt=17 +ProcStart dt=141933 p=2 p_seq=11 +ProcStop dt=13 +ProcStart dt=16744 p=2 p_seq=12 +GoUnblock dt=29 g=1 g_seq=17 stack=0 +GoUnblock dt=7 g=12 g_seq=2 stack=0 +GoStart dt=172 g=12 g_seq=3 +UserTaskEnd dt=15 task=20 stack=36 +UserRegionEnd dt=8 task=19 name_string=31 stack=28 +GoDestroy dt=2 +GoStart dt=11 g=1 g_seq=18 +UserTaskEnd dt=14 task=19 stack=62 +UserLog dt=16 task=4 key_string=24 value_string=101 stack=63 +UserTaskBegin dt=6 task=21 parent_task=4 name_string=26 stack=64 +UserLog dt=25 task=21 key_string=27 value_string=102 stack=65 +UserRegionBegin dt=7 task=21 name_string=29 stack=66 +GoCreate dt=23 new_g=54 new_stack=17 stack=67 +GoCreate dt=8 new_g=55 new_stack=17 stack=67 +GoCreate dt=17 new_g=56 new_stack=17 stack=67 +GoCreate dt=8 new_g=57 new_stack=17 stack=67 +GoCreate dt=7 new_g=58 new_stack=17 stack=67 +UserRegionEnd dt=4 task=21 name_string=29 stack=68 +GoBlock dt=9 reason_string=19 stack=69 +GoStart dt=7 g=58 g_seq=1 +UserRegionBegin dt=46 task=21 name_string=56 stack=26 +UserLog dt=8 task=21 key_string=24 value_string=103 stack=27 +UserRegionEnd dt=4 task=21 name_string=56 stack=28 +GoDestroy dt=1 +GoStart dt=3 g=54 g_seq=1 +UserRegionBegin dt=19 task=21 name_string=33 stack=26 +UserLog dt=7 task=21 key_string=24 value_string=104 stack=27 +UserRegionEnd dt=2 task=21 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=2 g=55 g_seq=1 +UserRegionBegin dt=17 task=21 name_string=35 stack=26 +UserLog dt=4 task=21 key_string=24 value_string=105 stack=27 +UserRegionEnd dt=2 task=21 name_string=35 stack=28 +GoDestroy dt=1 +GoStart dt=5 g=56 g_seq=1 +UserRegionBegin dt=16 task=21 name_string=31 stack=26 +UserLog dt=4 task=21 key_string=24 value_string=106 stack=27 +UserLog dt=3 task=21 key_string=24 value_string=107 stack=30 +UserTaskBegin dt=4 task=22 parent_task=21 name_string=26 stack=31 +UserLog dt=6 task=22 key_string=27 value_string=108 stack=32 +UserRegionBegin dt=4 task=22 name_string=29 stack=33 +UserRegionEnd dt=7 task=22 name_string=29 stack=34 +GoBlock dt=14 reason_string=19 stack=35 +GoStart dt=3 g=57 g_seq=1 +UserRegionBegin dt=22 task=21 name_string=30 stack=26 +UserLog dt=6 task=21 key_string=24 value_string=109 stack=27 +UserRegionEnd dt=2 task=21 name_string=30 stack=28 +GoDestroy dt=2 +ProcStop dt=10 +ProcStart dt=128031 p=4 p_seq=15 +ProcStop dt=16 +ProcStart dt=33758 p=2 p_seq=15 +ProcStop dt=18 +EventBatch gen=1 m=1986496 time=2753925246280 size=267 +ProcStart dt=549 p=0 p_seq=1 +GoStart dt=211 g=18 g_seq=1 +GoBlock dt=3533 reason_string=12 stack=21 +GoStart dt=41 g=21 g_seq=1 +GoBlock dt=150 reason_string=10 stack=22 +GoStart dt=93 g=20 g_seq=1 +GoSyscallBegin dt=51 p_seq=2 stack=23 +GoSyscallEnd dt=400 +GoBlock dt=582 reason_string=15 stack=25 +GoStart dt=26 g=23 g_seq=1 +HeapAlloc dt=50 heapalloc_value=1646592 +UserRegionBegin dt=2921 task=5 name_string=31 stack=26 +UserLog dt=28 task=5 key_string=24 value_string=37 stack=27 +UserLog dt=13 task=5 key_string=24 value_string=38 stack=30 +UserTaskBegin dt=15 task=6 parent_task=5 name_string=26 stack=31 +HeapAlloc dt=26 heapalloc_value=1687552 +UserLog dt=14 task=6 key_string=27 value_string=39 stack=32 +UserRegionBegin dt=9 task=6 name_string=29 stack=33 +UserRegionEnd dt=6 task=6 name_string=29 stack=34 +GoBlock dt=15 reason_string=19 stack=35 +ProcStop dt=30 +ProcStart dt=156949 p=4 p_seq=2 +GoUnblock dt=46 g=1 g_seq=1 stack=0 +GoStart dt=253 g=1 g_seq=2 +UserTaskEnd dt=27 task=5 stack=37 +UserLog dt=23 task=1 key_string=24 value_string=40 stack=38 +UserTaskBegin dt=14 task=7 parent_task=1 name_string=26 stack=39 +HeapAlloc dt=596 heapalloc_value=1695744 +HeapAlloc dt=18 heapalloc_value=1703936 +UserLog dt=17 task=7 key_string=27 value_string=41 stack=40 +UserRegionBegin dt=14 task=7 name_string=29 stack=41 +HeapAlloc dt=10 heapalloc_value=1712128 +HeapAlloc dt=17 heapalloc_value=1720320 +GoCreate dt=44 new_g=33 new_stack=17 stack=42 +GoCreate dt=175 new_g=34 new_stack=17 stack=42 +UserRegionEnd dt=50 task=7 name_string=29 stack=43 +GoBlock dt=9 reason_string=19 stack=44 +HeapAlloc dt=16 heapalloc_value=1728512 +GoStart dt=239 g=34 g_seq=1 +HeapAlloc dt=21 heapalloc_value=1736704 +UserRegionBegin dt=92 task=7 name_string=35 stack=26 +UserLog dt=15 task=7 key_string=24 value_string=42 stack=27 +UserRegionEnd dt=4 task=7 name_string=35 stack=28 +GoDestroy dt=2 +ProcStop dt=21 +ProcStart dt=800974 p=4 p_seq=10 +ProcStop dt=39 +ProcStart dt=158775 p=0 p_seq=15 +ProcStop dt=24 +ProcStart dt=159722 p=4 p_seq=13 +GoStart dt=254 g=13 g_seq=1 +UserRegionBegin dt=239 task=19 name_string=30 stack=26 +UserLog dt=23 task=19 key_string=24 value_string=100 stack=27 +UserRegionEnd dt=6 task=19 name_string=30 stack=28 +GoDestroy dt=7 +ProcStop dt=22 +EventBatch gen=1 m=1986495 time=2753925251756 size=320 +ProcStart dt=705 p=4 p_seq=1 +ProcStop dt=1279 +ProcStart dt=158975 p=0 p_seq=5 +ProcStop dt=23 +ProcStart dt=792 p=0 p_seq=6 +GoStart dt=187 g=33 g_seq=1 +UserRegionBegin dt=244 task=7 name_string=33 stack=26 +UserLog dt=32 task=7 key_string=24 value_string=43 stack=27 +UserRegionEnd dt=7 task=7 name_string=33 stack=28 +GoDestroy dt=5 +ProcStop dt=24 +ProcStart dt=160255 p=4 p_seq=4 +ProcStop dt=27 +ProcStart dt=159067 p=2 p_seq=5 +GoStart dt=222 g=37 g_seq=1 +UserRegionBegin dt=114 task=10 name_string=31 stack=26 +UserLog dt=16 task=10 key_string=24 value_string=60 stack=27 +UserLog dt=8 task=10 key_string=24 value_string=61 stack=30 +UserTaskBegin dt=8 task=11 parent_task=10 name_string=26 stack=31 +UserLog dt=19 task=11 key_string=27 value_string=62 stack=32 +UserRegionBegin dt=6 task=11 name_string=29 stack=33 +UserRegionEnd dt=7 task=11 name_string=29 stack=34 +GoBlock dt=15 reason_string=19 stack=35 +ProcStop dt=11 +ProcStart dt=160101 p=4 p_seq=7 +ProcStop dt=21 +ProcStart dt=159647 p=2 p_seq=7 +GoStart dt=277 g=43 g_seq=1 +UserRegionBegin dt=126 task=14 name_string=31 stack=26 +UserLog dt=21 task=14 key_string=24 value_string=77 stack=27 +UserLog dt=9 task=14 key_string=24 value_string=78 stack=30 +UserTaskBegin dt=8 task=15 parent_task=14 name_string=26 stack=31 +UserLog dt=17 task=15 key_string=27 value_string=79 stack=32 +UserRegionBegin dt=6 task=15 name_string=29 stack=33 +UserRegionEnd dt=8 task=15 name_string=29 stack=34 +GoBlock dt=23 reason_string=19 stack=35 +ProcStop dt=17 +ProcStart dt=159706 p=0 p_seq=14 +GoStart dt=229 g=52 g_seq=1 +UserRegionBegin dt=103 task=16 name_string=33 stack=26 +UserLog dt=20 task=16 key_string=24 value_string=83 stack=27 +UserRegionEnd dt=4 task=16 name_string=33 stack=28 +GoDestroy dt=3 +ProcStop dt=17 +ProcStart dt=319699 p=2 p_seq=10 +ProcStop dt=20 +ProcStart dt=158728 p=4 p_seq=14 +ProcStop dt=17 +ProcStart dt=110606 p=2 p_seq=13 +ProcStop dt=10 +ProcStart dt=16732 p=2 p_seq=14 +GoUnblock dt=45 g=18 g_seq=2 stack=0 +GoStart dt=184 g=18 g_seq=3 +GoBlock dt=114 reason_string=12 stack=21 +ProcStop dt=8 +ProcStart dt=16779 p=4 p_seq=16 +ProcStop dt=11 +ProcStart dt=16790 p=4 p_seq=17 +GoUnblock dt=23 g=1 g_seq=19 stack=0 +GoUnblock dt=8 g=56 g_seq=2 stack=0 +GoStart dt=142 g=56 g_seq=3 +UserTaskEnd dt=14 task=22 stack=36 +UserRegionEnd dt=8 task=21 name_string=31 stack=28 +GoDestroy dt=5 +GoStart dt=18 g=1 g_seq=20 +UserTaskEnd dt=17 task=21 stack=70 +UserTaskEnd dt=12 task=4 stack=71 +HeapAlloc dt=802 heapalloc_value=1835008 +HeapAlloc dt=41 heapalloc_value=1843200 +HeapAlloc dt=13 heapalloc_value=1851392 +EventBatch gen=1 m=1986494 time=2753925248778 size=47 +ProcStart dt=390 p=3 p_seq=1 +GoStart dt=1718 g=22 g_seq=1 +HeapAlloc dt=1807 heapalloc_value=1654784 +HeapAlloc dt=406 heapalloc_value=1671168 +HeapAlloc dt=15 heapalloc_value=1679360 +UserRegionBegin dt=49 task=5 name_string=35 stack=26 +UserLog dt=30 task=5 key_string=24 value_string=36 stack=27 +UserRegionEnd dt=5 task=5 name_string=35 stack=28 +GoDestroy dt=5 +ProcStop dt=42 +EventBatch gen=1 m=1986492 time=2753925244400 size=582 +ProcStatus dt=67 p=1 pstatus=1 +GoStatus dt=4 g=1 m=1986492 gstatus=2 +ProcsChange dt=220 procs_value=8 stack=1 +STWBegin dt=127 kind_string=21 stack=2 +HeapGoal dt=3 heapgoal_value=4194304 +ProcStatus dt=2 p=0 pstatus=2 +ProcStatus dt=2 p=2 pstatus=2 +ProcStatus dt=1 p=3 pstatus=2 +ProcStatus dt=1 p=4 pstatus=2 +ProcStatus dt=1 p=5 pstatus=2 +ProcStatus dt=1 p=6 pstatus=2 +ProcStatus dt=1 p=7 pstatus=2 +ProcsChange dt=353 procs_value=8 stack=3 +STWEnd dt=277 +HeapAlloc dt=243 heapalloc_value=1605632 +HeapAlloc dt=24 heapalloc_value=1613824 +GoCreate dt=209 new_g=18 new_stack=4 stack=5 +GoCreate dt=561 new_g=19 new_stack=6 stack=7 +GoCreate dt=25 new_g=20 new_stack=8 stack=9 +UserTaskEnd dt=309 task=2 stack=10 +UserTaskBegin dt=26 task=3 parent_task=1 name_string=22 stack=11 +UserTaskBegin dt=918 task=4 parent_task=0 name_string=23 stack=12 +UserLog dt=461 task=0 key_string=24 value_string=25 stack=13 +UserTaskBegin dt=420 task=5 parent_task=0 name_string=26 stack=14 +UserLog dt=673 task=5 key_string=27 value_string=28 stack=15 +UserRegionBegin dt=15 task=5 name_string=29 stack=16 +HeapAlloc dt=51 heapalloc_value=1630208 +GoCreate dt=24 new_g=21 new_stack=17 stack=18 +GoCreate dt=17 new_g=22 new_stack=17 stack=18 +GoCreate dt=10 new_g=23 new_stack=17 stack=18 +GoCreate dt=9 new_g=24 new_stack=17 stack=18 +UserRegionEnd dt=549 task=5 name_string=29 stack=19 +GoBlock dt=14 reason_string=19 stack=20 +GoStart dt=378 g=24 g_seq=1 +HeapAlloc dt=65 heapalloc_value=1638400 +GoUnblock dt=559 g=21 g_seq=2 stack=24 +UserRegionBegin dt=1498 task=5 name_string=30 stack=26 +UserLog dt=35 task=5 key_string=24 value_string=32 stack=27 +UserRegionEnd dt=8 task=5 name_string=30 stack=28 +GoDestroy dt=5 +GoStart dt=24 g=21 g_seq=3 +UserRegionBegin dt=60 task=5 name_string=33 stack=26 +UserLog dt=7 task=5 key_string=24 value_string=34 stack=27 +UserRegionEnd dt=2 task=5 name_string=33 stack=28 +GoDestroy dt=2 +ProcStop dt=34 +ProcStart dt=141874 p=0 p_seq=3 +ProcStop dt=21 +ProcStart dt=16770 p=0 p_seq=4 +GoUnblock dt=29 g=23 g_seq=2 stack=0 +GoStart dt=176 g=23 g_seq=3 +UserTaskEnd dt=19 task=6 stack=36 +UserRegionEnd dt=14 task=5 name_string=31 stack=28 +GoDestroy dt=2 +ProcStop dt=12 +ProcStart dt=2251 p=4 p_seq=3 +ProcStop dt=22 +ProcStart dt=141952 p=2 p_seq=3 +ProcStop dt=27 +ProcStart dt=16789 p=2 p_seq=4 +GoUnblock dt=35 g=1 g_seq=3 stack=0 +GoStart dt=214 g=1 g_seq=4 +UserTaskEnd dt=26 task=7 stack=45 +UserLog dt=27 task=2 key_string=24 value_string=44 stack=46 +UserTaskBegin dt=10 task=8 parent_task=2 name_string=26 stack=47 +HeapAlloc dt=52 heapalloc_value=1744896 +HeapAlloc dt=22 heapalloc_value=1753088 +UserLog dt=13 task=8 key_string=27 value_string=45 stack=48 +UserRegionBegin dt=11 task=8 name_string=29 stack=49 +HeapAlloc dt=7 heapalloc_value=1761280 +HeapAlloc dt=18 heapalloc_value=1769472 +GoCreate dt=52 new_g=49 new_stack=17 stack=50 +GoCreate dt=12 new_g=50 new_stack=17 stack=50 +HeapAlloc dt=11 heapalloc_value=1777664 +GoCreate dt=9 new_g=51 new_stack=17 stack=50 +UserRegionEnd dt=9 task=8 name_string=29 stack=51 +GoBlock dt=11 reason_string=19 stack=52 +HeapAlloc dt=12 heapalloc_value=1785856 +GoStart dt=14 g=51 g_seq=1 +HeapAlloc dt=18 heapalloc_value=1794048 +UserRegionBegin dt=95 task=8 name_string=31 stack=26 +UserLog dt=22 task=8 key_string=24 value_string=46 stack=27 +UserLog dt=8 task=8 key_string=24 value_string=47 stack=30 +UserTaskBegin dt=5 task=9 parent_task=8 name_string=26 stack=31 +UserLog dt=7 task=9 key_string=27 value_string=48 stack=32 +UserRegionBegin dt=4 task=9 name_string=29 stack=33 +UserRegionEnd dt=7 task=9 name_string=29 stack=34 +HeapAlloc dt=11 heapalloc_value=1802240 +GoStop dt=674 reason_string=16 stack=53 +GoStart dt=12 g=51 g_seq=2 +GoBlock dt=8 reason_string=19 stack=35 +HeapAlloc dt=16 heapalloc_value=1810432 +ProcStop dt=8 +ProcStart dt=159907 p=0 p_seq=8 +ProcStop dt=25 +ProcStart dt=159186 p=2 p_seq=6 +GoUnblock dt=22 g=37 g_seq=2 stack=0 +GoStart dt=217 g=37 g_seq=3 +UserTaskEnd dt=19 task=11 stack=36 +UserRegionEnd dt=15 task=10 name_string=31 stack=28 +GoDestroy dt=5 +ProcStop dt=16 +ProcStart dt=160988 p=0 p_seq=11 +ProcStop dt=29 +ProcStart dt=158554 p=2 p_seq=8 +GoUnblock dt=38 g=1 g_seq=11 stack=0 +GoStart dt=240 g=1 g_seq=12 +UserTaskEnd dt=25 task=14 stack=37 +UserLog dt=23 task=1 key_string=24 value_string=80 stack=38 +UserTaskBegin dt=11 task=16 parent_task=1 name_string=26 stack=39 +UserLog dt=36 task=16 key_string=27 value_string=81 stack=40 +UserRegionBegin dt=13 task=16 name_string=29 stack=41 +GoCreate dt=39 new_g=52 new_stack=17 stack=42 +GoCreate dt=23 new_g=53 new_stack=17 stack=42 +UserRegionEnd dt=11 task=16 name_string=29 stack=43 +GoBlock dt=9 reason_string=19 stack=44 +GoStart dt=244 g=53 g_seq=1 +UserRegionBegin dt=101 task=16 name_string=35 stack=26 +UserLog dt=17 task=16 key_string=24 value_string=82 stack=27 +UserRegionEnd dt=4 task=16 name_string=35 stack=28 +GoDestroy dt=3 +ProcStop dt=28 +EventBatch gen=1 m=18446744073709551615 time=2753926855140 size=56 +GoStatus dt=74 g=2 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=3 m=18446744073709551615 gstatus=4 +GoStatus dt=1 g=4 m=18446744073709551615 gstatus=4 +GoStatus dt=1 g=17 m=18446744073709551615 gstatus=4 +EventBatch gen=1 m=18446744073709551615 time=2753926855560 size=1759 +Stacks +Stack id=45 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803566 func=114 file=113 line=44 +Stack id=22 nframes=7 + pc=4633935 func=115 file=116 line=90 + pc=4633896 func=117 file=118 line=223 + pc=4633765 func=119 file=118 line=216 + pc=4633083 func=120 file=118 line=131 + pc=4764601 func=121 file=122 line=152 + pc=4765335 func=123 file=122 line=238 + pc=4804612 func=124 file=113 line=70 +Stack id=9 nframes=2 + pc=4802543 func=125 file=126 line=128 + pc=4803332 func=114 file=113 line=30 +Stack id=71 nframes=2 + pc=4803671 func=110 file=111 line=80 + pc=4803666 func=114 file=113 line=51 +Stack id=10 nframes=2 + pc=4803415 func=110 file=111 line=80 + pc=4803410 func=114 file=113 line=33 +Stack id=18 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803543 func=114 file=113 line=43 +Stack id=37 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803543 func=114 file=113 line=43 +Stack id=31 nframes=4 + pc=4803865 func=112 file=113 line=61 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=55 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803609 func=114 file=113 line=46 +Stack id=47 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803589 func=114 file=113 line=45 +Stack id=38 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803566 func=114 file=113 line=44 +Stack id=56 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803609 func=114 file=113 line=46 +Stack id=33 nframes=4 + pc=4804022 func=112 file=113 line=67 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=44 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803566 func=114 file=113 line=44 +Stack id=3 nframes=4 + pc=4421707 func=132 file=133 line=1382 + pc=4533555 func=134 file=135 line=255 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=6 nframes=1 + pc=4539520 func=136 file=135 line=868 +Stack id=58 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803609 func=114 file=113 line=46 +Stack id=64 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803629 func=114 file=113 line=47 +Stack id=62 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803609 func=114 file=113 line=46 +Stack id=34 nframes=4 + pc=4804022 func=112 file=113 line=67 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=30 nframes=4 + pc=4803832 func=112 file=113 line=58 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=32 nframes=4 + pc=4803943 func=112 file=113 line=64 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=26 nframes=1 + pc=4804691 func=124 file=113 line=70 +Stack id=46 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803589 func=114 file=113 line=45 +Stack id=50 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803589 func=114 file=113 line=45 +Stack id=59 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803609 func=114 file=113 line=46 +Stack id=7 nframes=4 + pc=4539492 func=137 file=135 line=868 + pc=4533572 func=134 file=135 line=258 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=17 nframes=1 + pc=4804512 func=124 file=113 line=69 +Stack id=57 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803609 func=114 file=113 line=46 +Stack id=41 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803566 func=114 file=113 line=44 +Stack id=63 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803629 func=114 file=113 line=47 +Stack id=60 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803609 func=114 file=113 line=46 +Stack id=5 nframes=4 + pc=4542549 func=138 file=139 line=42 + pc=4533560 func=134 file=135 line=257 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=40 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803566 func=114 file=113 line=44 +Stack id=21 nframes=3 + pc=4217905 func=140 file=141 line=442 + pc=4539946 func=142 file=135 line=928 + pc=4542714 func=143 file=139 line=54 +Stack id=2 nframes=3 + pc=4533284 func=134 file=135 line=238 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=53 nframes=6 + pc=4247492 func=144 file=145 line=1374 + pc=4599676 func=130 file=131 line=186 + pc=4804036 func=112 file=113 line=83 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=20 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803543 func=114 file=113 line=43 +Stack id=70 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803629 func=114 file=113 line=47 +Stack id=15 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803543 func=114 file=113 line=43 +Stack id=65 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803629 func=114 file=113 line=47 +Stack id=28 nframes=1 + pc=4804691 func=124 file=113 line=70 +Stack id=48 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803589 func=114 file=113 line=45 +Stack id=61 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803609 func=114 file=113 line=46 +Stack id=13 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803543 func=114 file=113 line=43 +Stack id=29 nframes=3 + pc=4217905 func=140 file=141 line=442 + pc=4539946 func=142 file=135 line=928 + pc=4539559 func=136 file=135 line=871 +Stack id=51 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803589 func=114 file=113 line=45 +Stack id=42 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803566 func=114 file=113 line=44 +Stack id=14 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803543 func=114 file=113 line=43 +Stack id=39 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803566 func=114 file=113 line=44 +Stack id=49 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803589 func=114 file=113 line=45 +Stack id=52 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803589 func=114 file=113 line=45 +Stack id=24 nframes=7 + pc=4634510 func=146 file=116 line=223 + pc=4634311 func=117 file=118 line=240 + pc=4633765 func=119 file=118 line=216 + pc=4633083 func=120 file=118 line=131 + pc=4764601 func=121 file=122 line=152 + pc=4765335 func=123 file=122 line=238 + pc=4804612 func=124 file=113 line=70 +Stack id=43 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803566 func=114 file=113 line=44 +Stack id=19 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803543 func=114 file=113 line=43 +Stack id=69 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803629 func=114 file=113 line=47 +Stack id=16 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803543 func=114 file=113 line=43 +Stack id=54 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803589 func=114 file=113 line=45 +Stack id=35 nframes=5 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=27 nframes=3 + pc=4804862 func=129 file=113 line=71 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=4 nframes=1 + pc=4542656 func=143 file=139 line=42 +Stack id=8 nframes=1 + pc=4802720 func=147 file=126 line=128 +Stack id=66 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803629 func=114 file=113 line=47 +Stack id=1 nframes=4 + pc=4548715 func=148 file=149 line=255 + pc=4533263 func=134 file=135 line=237 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=67 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803629 func=114 file=113 line=47 +Stack id=23 nframes=7 + pc=4641050 func=150 file=151 line=964 + pc=4751591 func=152 file=153 line=209 + pc=4751583 func=154 file=155 line=736 + pc=4751136 func=156 file=155 line=380 + pc=4753008 func=157 file=158 line=46 + pc=4753000 func=159 file=160 line=183 + pc=4802778 func=147 file=126 line=134 +Stack id=11 nframes=1 + pc=4803445 func=114 file=113 line=36 +Stack id=68 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803629 func=114 file=113 line=47 +Stack id=36 nframes=5 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=12 nframes=1 + pc=4803492 func=114 file=113 line=39 +Stack id=25 nframes=1 + pc=4802788 func=147 file=126 line=130 +EventBatch gen=1 m=18446744073709551615 time=2753925243266 size=3466 +Strings +String id=1 + data="Not worker" +String id=2 + data="GC (dedicated)" +String id=3 + data="GC (fractional)" +String id=4 + data="GC (idle)" +String id=5 + data="unspecified" +String id=6 + data="forever" +String id=7 + data="network" +String id=8 + data="select" +String id=9 + data="sync.(*Cond).Wait" +String id=10 + data="sync" +String id=11 + data="chan send" +String id=12 + data="chan receive" +String id=13 + data="GC mark assist wait for work" +String id=14 + data="GC background sweeper wait" +String id=15 + data="system goroutine wait" +String id=16 + data="preempted" +String id=17 + data="wait for debug call" +String id=18 + data="wait until GC ends" +String id=19 + data="sleep" +String id=20 + data="runtime.Gosched" +String id=21 + data="start trace" +String id=22 + data="type2" +String id=23 + data="type3" +String id=24 + data="log" +String id=25 + data="before do" +String id=26 + data="do" +String id=27 + data="log2" +String id=28 + data="do" +String id=29 + data="fanout" +String id=30 + data="region3" +String id=31 + data="region2" +String id=32 + data="fanout region3" +String id=33 + data="region0" +String id=34 + data="fanout region0" +String id=35 + data="region1" +String id=36 + data="fanout region1" +String id=37 + data="fanout region2" +String id=38 + data="before do" +String id=39 + data="do" +String id=40 + data="before do" +String id=41 + data="do" +String id=42 + data="fanout region1" +String id=43 + data="fanout region0" +String id=44 + data="before do" +String id=45 + data="do" +String id=46 + data="fanout region2" +String id=47 + data="before do" +String id=48 + data="do" +String id=49 + data="fanout region0" +String id=50 + data="fanout region1" +String id=51 + data="before do" +String id=52 + data="do" +String id=53 + data="region5" +String id=54 + data="fanout region5" +String id=55 + data="fanout region3" +String id=56 + data="region4" +String id=57 + data="fanout region4" +String id=58 + data="fanout region0" +String id=59 + data="fanout region1" +String id=60 + data="fanout region2" +String id=61 + data="before do" +String id=62 + data="do" +String id=63 + data="before do" +String id=64 + data="do" +String id=65 + data="fanout region4" +String id=66 + data="fanout region0" +String id=67 + data="fanout region1" +String id=68 + data="fanout region2" +String id=69 + data="before do" +String id=70 + data="do" +String id=71 + data="fanout region3" +String id=72 + data="before do" +String id=73 + data="do" +String id=74 + data="fanout region3" +String id=75 + data="fanout region0" +String id=76 + data="fanout region1" +String id=77 + data="fanout region2" +String id=78 + data="before do" +String id=79 + data="do" +String id=80 + data="before do" +String id=81 + data="do" +String id=82 + data="fanout region1" +String id=83 + data="fanout region0" +String id=84 + data="before do" +String id=85 + data="do" +String id=86 + data="fanout region2" +String id=87 + data="before do" +String id=88 + data="do" +String id=89 + data="fanout region0" +String id=90 + data="fanout region1" +String id=91 + data="before do" +String id=92 + data="do" +String id=93 + data="fanout region5" +String id=94 + data="fanout region0" +String id=95 + data="fanout region1" +String id=96 + data="fanout region2" +String id=97 + data="before do" +String id=98 + data="do" +String id=99 + data="fanout region4" +String id=100 + data="fanout region3" +String id=101 + data="before do" +String id=102 + data="do" +String id=103 + data="fanout region4" +String id=104 + data="fanout region0" +String id=105 + data="fanout region1" +String id=106 + data="fanout region2" +String id=107 + data="before do" +String id=108 + data="do" +String id=109 + data="fanout region3" +String id=110 + data="runtime/trace.(*Task).End" +String id=111 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/annotation.go" +String id=112 + data="main.do" +String id=113 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/trace/v2/testdata/testprog/annotations-stress.go" +String id=114 + data="main.main" +String id=115 + data="sync.(*Mutex).Lock" +String id=116 + data="/usr/local/google/home/mknyszek/work/go-1/src/sync/mutex.go" +String id=117 + data="sync.(*Pool).pinSlow" +String id=118 + data="/usr/local/google/home/mknyszek/work/go-1/src/sync/pool.go" +String id=119 + data="sync.(*Pool).pin" +String id=120 + data="sync.(*Pool).Get" +String id=121 + data="fmt.newPrinter" +String id=122 + data="/usr/local/google/home/mknyszek/work/go-1/src/fmt/print.go" +String id=123 + data="fmt.Sprintf" +String id=124 + data="main.do.func1.1" +String id=125 + data="runtime/trace.Start" +String id=126 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" +String id=127 + data="main.do.func1" +String id=128 + data="runtime/trace.WithRegion" +String id=129 + data="main.do.func1.1.1" +String id=130 + data="time.Sleep" +String id=131 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/time.go" +String id=132 + data="runtime.startTheWorld" +String id=133 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" +String id=134 + data="runtime.StartTrace" +String id=135 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" +String id=136 + data="runtime.(*traceAdvancerState).start.func1" +String id=137 + data="runtime.(*traceAdvancerState).start" +String id=138 + data="runtime.traceStartReadCPU" +String id=139 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2cpu.go" +String id=140 + data="runtime.chanrecv1" +String id=141 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" +String id=142 + data="runtime.(*wakeableSleep).sleep" +String id=143 + data="runtime.traceStartReadCPU.func1" +String id=144 + data="runtime.newobject" +String id=145 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/malloc.go" +String id=146 + data="sync.(*Mutex).Unlock" +String id=147 + data="runtime/trace.Start.func1" +String id=148 + data="runtime.traceLocker.Gomaxprocs" +String id=149 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" +String id=150 + data="syscall.write" +String id=151 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/zsyscall_linux_amd64.go" +String id=152 + data="syscall.Write" +String id=153 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/syscall_unix.go" +String id=154 + data="internal/poll.ignoringEINTRIO" +String id=155 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_unix.go" +String id=156 + data="internal/poll.(*FD).Write" +String id=157 + data="os.(*File).write" +String id=158 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_posix.go" +String id=159 + data="os.(*File).Write" +String id=160 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file.go" diff --git a/src/internal/trace/testdata/tests/go122-annotations.test b/src/internal/trace/testdata/tests/go122-annotations.test new file mode 100644 index 0000000000..e468673497 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-annotations.test @@ -0,0 +1,299 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=18446744073709551615 time=28113086279559 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=167930 time=28113086277797 size=41 +ProcStart dt=505 p=1 p_seq=1 +GoStart dt=303 g=7 g_seq=1 +HeapAlloc dt=646 heapalloc_value=1892352 +HeapAlloc dt=149 heapalloc_value=1900544 +GoBlock dt=146 reason_string=12 stack=24 +GoStart dt=14 g=6 g_seq=1 +HeapAlloc dt=16 heapalloc_value=1908736 +GoBlock dt=347 reason_string=12 stack=25 +EventBatch gen=1 m=167928 time=28113086279032 size=10 +ProcStart dt=451 p=2 p_seq=1 +GoStart dt=188 g=8 g_seq=1 +EventBatch gen=1 m=167926 time=28113086275999 size=324 +ProcStatus dt=295 p=0 pstatus=1 +GoStatus dt=5 g=1 m=167926 gstatus=2 +ProcsChange dt=405 procs_value=48 stack=1 +STWBegin dt=65 kind_string=21 stack=2 +HeapGoal dt=2 heapgoal_value=4194304 +ProcStatus dt=4 p=1 pstatus=2 +ProcStatus dt=1 p=2 pstatus=2 +ProcStatus dt=1 p=3 pstatus=2 +ProcStatus dt=1 p=4 pstatus=2 +ProcStatus dt=1 p=5 pstatus=2 +ProcStatus dt=1 p=6 pstatus=2 +ProcStatus dt=1 p=7 pstatus=2 +ProcStatus dt=1 p=8 pstatus=2 +ProcStatus dt=1 p=9 pstatus=2 +ProcStatus dt=1 p=10 pstatus=2 +ProcStatus dt=1 p=11 pstatus=2 +ProcStatus dt=1 p=12 pstatus=2 +ProcStatus dt=1 p=13 pstatus=2 +ProcStatus dt=1 p=14 pstatus=2 +ProcStatus dt=1 p=15 pstatus=2 +ProcStatus dt=1 p=16 pstatus=2 +ProcStatus dt=1 p=17 pstatus=2 +ProcStatus dt=1 p=18 pstatus=2 +ProcStatus dt=1 p=19 pstatus=2 +ProcStatus dt=1 p=20 pstatus=2 +ProcStatus dt=1 p=21 pstatus=2 +ProcStatus dt=1 p=22 pstatus=2 +ProcStatus dt=1 p=23 pstatus=2 +ProcStatus dt=1 p=24 pstatus=2 +ProcStatus dt=1 p=25 pstatus=2 +ProcStatus dt=1 p=26 pstatus=2 +ProcStatus dt=1 p=27 pstatus=2 +ProcStatus dt=1 p=28 pstatus=2 +ProcStatus dt=1 p=29 pstatus=2 +ProcStatus dt=1 p=30 pstatus=2 +ProcStatus dt=1 p=31 pstatus=2 +ProcStatus dt=1 p=32 pstatus=2 +ProcStatus dt=1 p=33 pstatus=2 +ProcStatus dt=1 p=34 pstatus=2 +ProcStatus dt=1 p=35 pstatus=2 +ProcStatus dt=1 p=36 pstatus=2 +ProcStatus dt=1 p=37 pstatus=2 +ProcStatus dt=1 p=38 pstatus=2 +ProcStatus dt=1 p=39 pstatus=2 +ProcStatus dt=1 p=40 pstatus=2 +ProcStatus dt=1 p=41 pstatus=2 +ProcStatus dt=1 p=42 pstatus=2 +ProcStatus dt=1 p=43 pstatus=2 +ProcStatus dt=1 p=44 pstatus=2 +ProcStatus dt=1 p=45 pstatus=2 +ProcStatus dt=1 p=46 pstatus=2 +ProcStatus dt=1 p=47 pstatus=2 +ProcsChange dt=1 procs_value=48 stack=3 +STWEnd dt=184 +GoCreate dt=252 new_g=6 new_stack=4 stack=5 +GoCreate dt=78 new_g=7 new_stack=6 stack=7 +GoCreate dt=73 new_g=8 new_stack=8 stack=9 +UserTaskBegin dt=71 task=1 parent_task=0 name_string=22 stack=10 +UserRegionBegin dt=535 task=1 name_string=23 stack=11 +HeapAlloc dt=26 heapalloc_value=1884160 +GoCreate dt=8 new_g=9 new_stack=12 stack=13 +GoBlock dt=249 reason_string=10 stack=14 +GoStart dt=8 g=9 g_seq=1 +UserRegionBegin dt=286 task=1 name_string=24 stack=15 +UserRegionBegin dt=244 task=1 name_string=25 stack=16 +UserRegionBegin dt=6 task=1 name_string=26 stack=17 +UserLog dt=6 task=1 key_string=27 value_string=28 stack=18 +UserRegionEnd dt=4 task=1 name_string=26 stack=19 +UserRegionEnd dt=315 task=1 name_string=25 stack=20 +UserTaskEnd dt=5 task=1 stack=21 +GoUnblock dt=11 g=1 g_seq=1 stack=22 +GoDestroy dt=6 +GoStart dt=10 g=1 g_seq=2 +UserRegionBegin dt=278 task=0 name_string=29 stack=23 +EventBatch gen=1 m=18446744073709551615 time=28113086280061 size=57 +GoStatus dt=318 g=2 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=3 m=18446744073709551615 gstatus=4 +GoStatus dt=1 g=4 m=18446744073709551615 gstatus=4 +GoStatus dt=1 g=5 m=18446744073709551615 gstatus=4 +EventBatch gen=1 m=18446744073709551615 time=28113086280852 size=488 +Stacks +Stack id=17 nframes=3 + pc=4816080 func=30 file=31 line=45 + pc=4813660 func=32 file=33 line=141 + pc=4815908 func=34 file=31 line=43 +Stack id=8 nframes=1 + pc=4814528 func=35 file=36 line=128 +Stack id=9 nframes=2 + pc=4814351 func=37 file=36 line=128 + pc=4815228 func=38 file=31 line=27 +Stack id=24 nframes=3 + pc=4217457 func=39 file=40 line=442 + pc=4544973 func=41 file=42 line=918 + pc=4544806 func=43 file=42 line=871 +Stack id=7 nframes=4 + pc=4544740 func=44 file=42 line=868 + pc=4538792 func=45 file=42 line=258 + pc=4814277 func=37 file=36 line=125 + pc=4815228 func=38 file=31 line=27 +Stack id=22 nframes=3 + pc=4642148 func=46 file=47 line=81 + pc=4816326 func=48 file=47 line=87 + pc=4815941 func=34 file=31 line=50 +Stack id=11 nframes=1 + pc=4815364 func=38 file=31 line=34 +Stack id=5 nframes=4 + pc=4547349 func=49 file=50 line=42 + pc=4538780 func=45 file=42 line=257 + pc=4814277 func=37 file=36 line=125 + pc=4815228 func=38 file=31 line=27 +Stack id=23 nframes=1 + pc=4815568 func=38 file=31 line=54 +Stack id=3 nframes=4 + pc=4421860 func=51 file=52 line=1360 + pc=4538775 func=45 file=42 line=255 + pc=4814277 func=37 file=36 line=125 + pc=4815228 func=38 file=31 line=27 +Stack id=21 nframes=2 + pc=4816228 func=53 file=33 line=80 + pc=4815926 func=34 file=31 line=50 +Stack id=1 nframes=4 + pc=4553515 func=54 file=55 line=255 + pc=4538503 func=45 file=42 line=237 + pc=4814277 func=37 file=36 line=125 + pc=4815228 func=38 file=31 line=27 +Stack id=12 nframes=1 + pc=4815680 func=34 file=31 line=37 +Stack id=6 nframes=1 + pc=4544768 func=43 file=42 line=868 +Stack id=2 nframes=3 + pc=4538523 func=45 file=42 line=238 + pc=4814277 func=37 file=36 line=125 + pc=4815228 func=38 file=31 line=27 +Stack id=13 nframes=1 + pc=4815492 func=38 file=31 line=37 +Stack id=4 nframes=1 + pc=4547456 func=56 file=50 line=42 +Stack id=14 nframes=2 + pc=4642407 func=57 file=47 line=116 + pc=4815502 func=38 file=31 line=51 +Stack id=18 nframes=5 + pc=4816147 func=58 file=31 line=46 + pc=4813660 func=32 file=33 line=141 + pc=4816080 func=30 file=31 line=45 + pc=4813660 func=32 file=33 line=141 + pc=4815908 func=34 file=31 line=43 +Stack id=20 nframes=1 + pc=4815908 func=34 file=31 line=43 +Stack id=25 nframes=3 + pc=4217457 func=39 file=40 line=442 + pc=4544973 func=41 file=42 line=918 + pc=4547514 func=56 file=50 line=54 +Stack id=16 nframes=1 + pc=4815908 func=34 file=31 line=43 +Stack id=15 nframes=1 + pc=4815838 func=34 file=31 line=41 +Stack id=19 nframes=3 + pc=4816080 func=30 file=31 line=45 + pc=4813660 func=32 file=33 line=141 + pc=4815908 func=34 file=31 line=43 +Stack id=10 nframes=1 + pc=4815332 func=38 file=31 line=33 +EventBatch gen=1 m=18446744073709551615 time=28113086274600 size=1620 +Strings +String id=1 + data="Not worker" +String id=2 + data="GC (dedicated)" +String id=3 + data="GC (fractional)" +String id=4 + data="GC (idle)" +String id=5 + data="unspecified" +String id=6 + data="forever" +String id=7 + data="network" +String id=8 + data="select" +String id=9 + data="sync.(*Cond).Wait" +String id=10 + data="sync" +String id=11 + data="chan send" +String id=12 + data="chan receive" +String id=13 + data="GC mark assist wait for work" +String id=14 + data="GC background sweeper wait" +String id=15 + data="system goroutine wait" +String id=16 + data="preempted" +String id=17 + data="wait for debug call" +String id=18 + data="wait until GC ends" +String id=19 + data="sleep" +String id=20 + data="runtime.Gosched" +String id=21 + data="start trace" +String id=22 + data="task0" +String id=23 + data="task0 region" +String id=24 + data="unended region" +String id=25 + data="region0" +String id=26 + data="region1" +String id=27 + data="key0" +String id=28 + data="0123456789abcdef" +String id=29 + data="post-existing region" +String id=30 + data="main.main.func1.1" +String id=31 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/trace/v2/testdata/testprog/annotations.go" +String id=32 + data="runtime/trace.WithRegion" +String id=33 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/annotation.go" +String id=34 + data="main.main.func1" +String id=35 + data="runtime/trace.Start.func1" +String id=36 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" +String id=37 + data="runtime/trace.Start" +String id=38 + data="main.main" +String id=39 + data="runtime.chanrecv1" +String id=40 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" +String id=41 + data="runtime.(*wakeableSleep).sleep" +String id=42 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" +String id=43 + data="runtime.(*traceAdvancerState).start.func1" +String id=44 + data="runtime.(*traceAdvancerState).start" +String id=45 + data="runtime.StartTrace" +String id=46 + data="sync.(*WaitGroup).Add" +String id=47 + data="/usr/local/google/home/mknyszek/work/go-1/src/sync/waitgroup.go" +String id=48 + data="sync.(*WaitGroup).Done" +String id=49 + data="runtime.traceStartReadCPU" +String id=50 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2cpu.go" +String id=51 + data="runtime.startTheWorld" +String id=52 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" +String id=53 + data="runtime/trace.(*Task).End" +String id=54 + data="runtime.traceLocker.Gomaxprocs" +String id=55 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" +String id=56 + data="runtime.traceStartReadCPU.func1" +String id=57 + data="sync.(*WaitGroup).Wait" +String id=58 + data="main.main.func1.1.1" diff --git a/src/internal/trace/testdata/tests/go122-confuse-seq-across-generations.test b/src/internal/trace/testdata/tests/go122-confuse-seq-across-generations.test new file mode 100644 index 0000000000..c0d6f0d3dd --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-confuse-seq-across-generations.test @@ -0,0 +1,36 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=13 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=2 +GoStop dt=1 reason_string=1 stack=0 +EventBatch gen=1 m=1 time=0 size=12 +ProcStatus dt=1 p=1 pstatus=1 +GoStart dt=1 g=1 g_seq=1 +GoStop dt=1 reason_string=1 stack=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=12 +Strings +String id=1 + data="whatever" +EventBatch gen=2 m=1 time=3 size=8 +ProcStatus dt=1 p=1 pstatus=1 +GoStart dt=1 g=1 g_seq=2 +EventBatch gen=2 m=0 time=5 size=17 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=1 +GoStart dt=1 g=1 g_seq=1 +GoStop dt=1 reason_string=1 stack=0 +EventBatch gen=2 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=2 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=2 m=18446744073709551615 time=0 size=12 +Strings +String id=1 + data="whatever" diff --git a/src/internal/trace/testdata/tests/go122-create-syscall-reuse-thread-id.test b/src/internal/trace/testdata/tests/go122-create-syscall-reuse-thread-id.test new file mode 100644 index 0000000000..1820738384 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-create-syscall-reuse-thread-id.test @@ -0,0 +1,23 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=37 +GoCreateSyscall dt=1 new_g=4 +GoSyscallEndBlocked dt=1 +ProcStatus dt=1 p=0 pstatus=2 +ProcStart dt=1 p=0 p_seq=1 +GoStatus dt=1 g=4 m=18446744073709551615 gstatus=1 +GoStart dt=1 g=4 g_seq=1 +GoSyscallBegin dt=1 p_seq=2 stack=0 +GoDestroySyscall dt=1 +EventBatch gen=1 m=0 time=0 size=13 +ProcStatus dt=1 p=1 pstatus=2 +ProcStart dt=1 p=1 p_seq=1 +ProcSteal dt=1 p=0 p_seq=3 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-create-syscall-with-p.test b/src/internal/trace/testdata/tests/go122-create-syscall-with-p.test new file mode 100644 index 0000000000..9b329b8bae --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-create-syscall-with-p.test @@ -0,0 +1,22 @@ +-- expect -- +FAILURE ".*expected a proc but didn't have one.*" +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=34 +GoCreateSyscall dt=1 new_g=4 +ProcStatus dt=1 p=0 pstatus=2 +ProcStart dt=1 p=0 p_seq=1 +GoSyscallEndBlocked dt=1 +GoStart dt=1 g=4 g_seq=1 +GoSyscallBegin dt=1 p_seq=2 stack=0 +GoDestroySyscall dt=1 +GoCreateSyscall dt=1 new_g=4 +GoSyscallEnd dt=1 +GoSyscallBegin dt=1 p_seq=3 stack=0 +GoDestroySyscall dt=1 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-fail-first-gen-first.test b/src/internal/trace/testdata/tests/go122-fail-first-gen-first.test new file mode 100644 index 0000000000..cc4240de40 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-fail-first-gen-first.test @@ -0,0 +1,9 @@ +-- expect -- +FAILURE "expected a proc but didn't have one" +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=0 time=0 size=5 +GoCreate dt=0 new_g=1 new_stack=0 stack=0 +EventBatch gen=2 m=0 time=0 size=50 diff --git a/src/internal/trace/testdata/tests/go122-gc-stress.test b/src/internal/trace/testdata/tests/go122-gc-stress.test new file mode 100644 index 0000000000..d5e7266f1e --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-gc-stress.test @@ -0,0 +1,4207 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=3 m=18446744073709551615 time=28114950954550 size=5 +Frequency freq=15625000 +EventBatch gen=3 m=169438 time=28114950899454 size=615 +ProcStatus dt=2 p=47 pstatus=1 +GoStatus dt=1 g=111 m=169438 gstatus=2 +GCMarkAssistActive dt=1 g=111 +GCMarkAssistEnd dt=1 +HeapAlloc dt=38 heapalloc_value=191159744 +HeapAlloc dt=134 heapalloc_value=191192512 +GCMarkAssistBegin dt=60 stack=3 +GoStop dt=2288 reason_string=20 stack=9 +GoStart dt=15 g=111 g_seq=1 +GCMarkAssistEnd dt=1860 +HeapAlloc dt=46 heapalloc_value=191585728 +GCMarkAssistBegin dt=35 stack=3 +GoBlock dt=32 reason_string=13 stack=11 +GoUnblock dt=14 g=57 g_seq=5 stack=0 +GoStart dt=9 g=57 g_seq=6 +GoLabel dt=3 label_string=2 +GoBlock dt=2925 reason_string=15 stack=5 +GoUnblock dt=12 g=57 g_seq=7 stack=0 +GoStart dt=5 g=57 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=391 reason_string=15 stack=5 +GoUnblock dt=15 g=57 g_seq=9 stack=0 +GoStart dt=7 g=57 g_seq=10 +GoLabel dt=1 label_string=2 +GoBlock dt=307 reason_string=15 stack=5 +GoUnblock dt=7 g=57 g_seq=11 stack=0 +GoStart dt=3 g=57 g_seq=12 +GoLabel dt=2 label_string=2 +GoBlock dt=1049 reason_string=15 stack=5 +GoUnblock dt=23 g=58 g_seq=7 stack=0 +GoStart dt=8 g=58 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=1126 reason_string=15 stack=5 +GoUnblock dt=12 g=53 g_seq=3 stack=0 +GoStart dt=5 g=53 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=1751 reason_string=15 stack=5 +GoUnblock dt=12 g=53 g_seq=5 stack=0 +GoStart dt=6 g=53 g_seq=6 +GoLabel dt=3 label_string=2 +GoBlock dt=119 reason_string=15 stack=5 +GoStart dt=15 g=88 g_seq=4 +GoBlock dt=50 reason_string=13 stack=11 +GoUnblock dt=1212 g=54 g_seq=15 stack=0 +GoStart dt=6 g=54 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=2984 reason_string=15 stack=5 +GoUnblock dt=2696 g=52 g_seq=21 stack=0 +GoStart dt=3 g=52 g_seq=22 +GoLabel dt=1 label_string=4 +GoBlock dt=2013 reason_string=15 stack=5 +GoStart dt=18 g=98 g_seq=6 +GCMarkAssistEnd dt=6 +HeapAlloc dt=44 heapalloc_value=192003520 +GCMarkAssistBegin dt=54 stack=3 +GoBlock dt=481 reason_string=13 stack=11 +GoUnblock dt=51 g=14 g_seq=17 stack=0 +GoStart dt=4 g=14 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=3954 reason_string=15 stack=5 +GoUnblock dt=59 g=57 g_seq=41 stack=0 +GoStart dt=8 g=57 g_seq=42 +GoLabel dt=1 label_string=4 +GoBlock dt=63 reason_string=15 stack=5 +GoUnblock dt=11 g=57 g_seq=43 stack=0 +GoStart dt=4 g=57 g_seq=44 +GoLabel dt=1 label_string=2 +GoBlock dt=3186 reason_string=15 stack=5 +GoUnblock dt=11 g=57 g_seq=45 stack=0 +GoStart dt=3 g=57 g_seq=46 +GoLabel dt=1 label_string=2 +GoBlock dt=9 reason_string=15 stack=5 +ProcStop dt=60 +ProcStart dt=50 p=47 p_seq=1 +GoUnblock dt=9 g=22 g_seq=33 stack=0 +GoStart dt=4 g=22 g_seq=34 +GoLabel dt=1 label_string=4 +GoBlock dt=97 reason_string=15 stack=5 +GoUnblock dt=9 g=22 g_seq=35 stack=0 +GoStart dt=5 g=22 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=2605 reason_string=15 stack=5 +GoUnblock dt=10 g=22 g_seq=37 stack=0 +GoStart dt=4 g=22 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=13 reason_string=15 stack=5 +ProcStop dt=37 +ProcStart dt=582 p=47 p_seq=2 +GoStart dt=504 g=97 g_seq=4 +GCMarkAssistEnd dt=5 +GCMarkAssistBegin dt=34 stack=3 +GoBlock dt=279 reason_string=13 stack=11 +ProcStop dt=30 +ProcStart dt=3780 p=47 p_seq=3 +GoUnblock dt=9 g=71 g_seq=25 stack=0 +GoStart dt=128 g=71 g_seq=26 +GoLabel dt=1 label_string=2 +GoBlock dt=210 reason_string=15 stack=5 +GoUnblock dt=8 g=71 g_seq=27 stack=0 +GoStart dt=1 g=71 g_seq=28 +GoLabel dt=1 label_string=2 +GoBlock dt=1627 reason_string=15 stack=5 +GoStart dt=27 g=105 g_seq=6 +GCMarkAssistEnd dt=3 +HeapAlloc dt=44 heapalloc_value=192477912 +GCMarkAssistBegin dt=77 stack=3 +GoStop dt=873 reason_string=20 stack=9 +GoUnblock dt=12 g=23 g_seq=47 stack=0 +GoStart dt=3 g=23 g_seq=48 +GoLabel dt=1 label_string=2 +GoBlock dt=36 reason_string=15 stack=5 +GoUnblock dt=6 g=23 g_seq=49 stack=0 +GoStart dt=1 g=23 g_seq=50 +GoLabel dt=1 label_string=2 +GoBlock dt=9 reason_string=15 stack=5 +GoUnblock dt=8 g=23 g_seq=51 stack=0 +GoStart dt=3 g=23 g_seq=52 +GoLabel dt=1 label_string=2 +GoBlock dt=15 reason_string=15 stack=5 +GoStart dt=10 g=105 g_seq=7 +GoStop dt=16 reason_string=20 stack=9 +GoUnblock dt=7 g=23 g_seq=53 stack=0 +GoStart dt=3 g=23 g_seq=54 +GoLabel dt=1 label_string=2 +GoBlock dt=10 reason_string=15 stack=5 +GoUnblock dt=12 g=23 g_seq=55 stack=0 +GoStart dt=3 g=23 g_seq=56 +GoLabel dt=1 label_string=2 +GoBlock dt=9 reason_string=15 stack=5 +GoUnblock dt=4 g=23 g_seq=57 stack=0 +GoStart dt=1 g=23 g_seq=58 +GoLabel dt=1 label_string=2 +GoBlock dt=4554 reason_string=15 stack=5 +GoStart dt=14 g=105 g_seq=10 +GCMarkAssistEnd dt=5 +HeapAlloc dt=65 heapalloc_value=193682136 +GCMarkAssistBegin dt=16 stack=3 +GoBlock dt=44 reason_string=13 stack=11 +GoStart dt=15 g=83 g_seq=8 +HeapAlloc dt=221 heapalloc_value=194173656 +HeapAlloc dt=1927 heapalloc_value=195443416 +GoStop dt=5838 reason_string=16 stack=6 +GoStart dt=51 g=83 g_seq=9 +GCMarkAssistBegin dt=12 stack=3 +GoBlock dt=35 reason_string=10 stack=18 +GoStart dt=70 g=87 g_seq=6 +GCMarkAssistBegin dt=14 stack=3 +GoBlock dt=35 reason_string=13 stack=11 +ProcStop dt=77 +EventBatch gen=3 m=169436 time=28114950894898 size=160 +ProcStatus dt=2 p=34 pstatus=1 +GoStatus dt=3 g=107 m=169436 gstatus=2 +GCMarkAssistBegin dt=15 stack=3 +GoBlock dt=4050 reason_string=13 stack=11 +GoStatus dt=20 g=23 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=23 g_seq=1 stack=0 +GoStart dt=8 g=23 g_seq=2 +GoLabel dt=1 label_string=2 +GoUnblock dt=2316 g=81 g_seq=1 stack=12 +GoBlock dt=626 reason_string=15 stack=5 +GoUnblock dt=9 g=23 g_seq=3 stack=0 +GoStart dt=9 g=23 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=3975 reason_string=15 stack=5 +GoUnblock dt=35 g=23 g_seq=5 stack=0 +GoStart dt=6 g=23 g_seq=6 +GoLabel dt=1 label_string=2 +GoBlock dt=142 reason_string=15 stack=5 +GoUnblock dt=9 g=23 g_seq=7 stack=0 +GoStart dt=4 g=23 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=3815 reason_string=15 stack=5 +GoUnblock dt=10 g=23 g_seq=9 stack=0 +GoStart dt=6 g=23 g_seq=10 +GoLabel dt=1 label_string=2 +GoBlock dt=3560 reason_string=15 stack=5 +GoUnblock dt=8 g=23 g_seq=11 stack=0 +GoStart dt=4 g=23 g_seq=12 +GoLabel dt=3 label_string=2 +GoBlock dt=2781 reason_string=15 stack=5 +GoUnblock dt=13 g=23 g_seq=13 stack=0 +GoStart dt=4 g=23 g_seq=14 +GoLabel dt=1 label_string=2 +GoBlock dt=1277 reason_string=15 stack=5 +ProcStop dt=16 +EventBatch gen=3 m=169435 time=28114950897148 size=522 +ProcStatus dt=2 p=24 pstatus=1 +GoStatus dt=2 g=122 m=169435 gstatus=2 +GCMarkAssistActive dt=1 g=122 +GCMarkAssistEnd dt=3 +HeapAlloc dt=24 heapalloc_value=190602688 +GCMarkAssistBegin dt=95 stack=3 +GCMarkAssistEnd dt=4651 +GCMarkAssistBegin dt=50 stack=3 +GoBlock dt=2931 reason_string=13 stack=11 +ProcStop dt=1401 +ProcStart dt=18 p=24 p_seq=1 +GoUnblock dt=3524 g=28 g_seq=5 stack=0 +GoStart dt=10 g=28 g_seq=6 +GoLabel dt=1 label_string=4 +GoBlock dt=42 reason_string=15 stack=5 +GoUnblock dt=1162 g=24 g_seq=11 stack=0 +GoStart dt=7 g=24 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=3050 reason_string=15 stack=5 +GoUnblock dt=5301 g=67 g_seq=15 stack=0 +GoStart dt=4 g=67 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=40 reason_string=15 stack=5 +ProcStop dt=64 +ProcStart dt=841 p=24 p_seq=2 +GoStatus dt=58 g=16 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=16 g_seq=1 stack=0 +GoStart dt=273 g=16 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=139 reason_string=15 stack=5 +ProcStop dt=52 +ProcStart dt=97 p=24 p_seq=3 +GoUnblock dt=5 g=16 g_seq=3 stack=0 +GoStart dt=2 g=16 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=471 reason_string=15 stack=5 +GoUnblock dt=58 g=16 g_seq=5 stack=0 +GoStart dt=6 g=16 g_seq=6 +GoLabel dt=3 label_string=4 +GoBlock dt=912 reason_string=15 stack=5 +GoUnblock dt=9 g=16 g_seq=7 stack=0 +GoStart dt=6 g=16 g_seq=8 +GoLabel dt=1 label_string=2 +GoUnblock dt=6571 g=113 g_seq=5 stack=12 +GoBlock dt=22 reason_string=15 stack=5 +ProcStop dt=73 +ProcStart dt=22914 p=30 p_seq=16 +GoStart dt=342 g=117 g_seq=4 +GCMarkAssistEnd dt=8 +HeapAlloc dt=67 heapalloc_value=196467152 +GoStop dt=5253 reason_string=16 stack=6 +GoStart dt=44 g=128 g_seq=7 +GCMarkAssistBegin dt=21 stack=3 +GoBlock dt=37 reason_string=10 stack=18 +GoStart dt=7 g=130 g_seq=5 +GoBlock dt=182 reason_string=10 stack=20 +ProcStop dt=81 +ProcStart dt=8287 p=2 p_seq=2 +GoStart dt=164 g=82 g_seq=11 +GCMarkAssistEnd dt=8 +HeapAlloc dt=169 heapalloc_value=104038048 +HeapAlloc dt=135 heapalloc_value=104189856 +HeapAlloc dt=126 heapalloc_value=104287136 +HeapAlloc dt=24 heapalloc_value=104308256 +HeapAlloc dt=28 heapalloc_value=104313888 +HeapAlloc dt=14 heapalloc_value=104399904 +GCSweepBegin dt=43 stack=28 +GCSweepEnd dt=8 swept_value=8192 reclaimed_value=8192 +HeapAlloc dt=4 heapalloc_value=104473632 +HeapAlloc dt=58 heapalloc_value=104510496 +HeapAlloc dt=22 heapalloc_value=104534432 +HeapAlloc dt=51 heapalloc_value=104654624 +GCSweepBegin dt=146 stack=28 +GCSweepEnd dt=8 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=4 heapalloc_value=104878624 +HeapAlloc dt=42 heapalloc_value=105007648 +HeapAlloc dt=29 heapalloc_value=105077280 +HeapAlloc dt=36 heapalloc_value=105105952 +HeapAlloc dt=44 heapalloc_value=105242784 +HeapAlloc dt=58 heapalloc_value=105431200 +HeapAlloc dt=128 heapalloc_value=105593760 +HeapAlloc dt=199 heapalloc_value=106209440 +GCSweepBegin dt=155 stack=28 +GCSweepEnd dt=13 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=106666272 +HeapAlloc dt=77 heapalloc_value=106901152 +HeapAlloc dt=64 heapalloc_value=107211808 +HeapAlloc dt=133 heapalloc_value=107661088 +HeapAlloc dt=34 heapalloc_value=107722528 +HeapAlloc dt=108 heapalloc_value=108207392 +GCSweepBegin dt=202 stack=28 +GCSweepEnd dt=13 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=108742816 +HeapAlloc dt=112 heapalloc_value=109093664 +HeapAlloc dt=207 heapalloc_value=109913120 +HeapAlloc dt=271 heapalloc_value=110834560 +HeapAlloc dt=212 heapalloc_value=111566720 +HeapAlloc dt=148 heapalloc_value=112190720 +HeapAlloc dt=74 heapalloc_value=112528128 +HeapAlloc dt=143 heapalloc_value=113050240 +HeapAlloc dt=19 heapalloc_value=113194368 +HeapAlloc dt=135 heapalloc_value=113615232 +GCSweepBegin dt=251 stack=27 +EventBatch gen=3 m=169434 time=28114950909315 size=660 +ProcStatus dt=2 p=7 pstatus=1 +GoStatus dt=2 g=71 m=169434 gstatus=2 +GoBlock dt=6 reason_string=15 stack=5 +GoUnblock dt=2633 g=53 g_seq=7 stack=0 +GoStart dt=7 g=53 g_seq=8 +GoLabel dt=3 label_string=4 +GoBlock dt=127 reason_string=15 stack=5 +GoUnblock dt=1358 g=52 g_seq=15 stack=0 +GoStart dt=7 g=52 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=27 reason_string=15 stack=5 +GoStart dt=1150 g=93 g_seq=4 +GCMarkAssistEnd dt=7 +HeapAlloc dt=39 heapalloc_value=191897024 +GCMarkAssistBegin dt=27 stack=3 +GoStop dt=894 reason_string=20 stack=9 +GoStart dt=13 g=93 g_seq=5 +GoBlock dt=150 reason_string=13 stack=11 +ProcStop dt=57 +ProcStart dt=14 p=7 p_seq=1 +ProcStop dt=4205 +ProcStart dt=18 p=7 p_seq=2 +GoUnblock dt=4 g=22 g_seq=17 stack=0 +GoStart dt=172 g=22 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=1298 reason_string=15 stack=5 +GoUnblock dt=12 g=22 g_seq=19 stack=0 +GoStart dt=7 g=22 g_seq=20 +GoLabel dt=1 label_string=2 +GoBlock dt=108 reason_string=15 stack=5 +GoUnblock dt=9 g=22 g_seq=21 stack=0 +GoStart dt=4 g=22 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=309 reason_string=15 stack=5 +GoUnblock dt=19 g=57 g_seq=35 stack=0 +GoStart dt=6 g=57 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=26 reason_string=15 stack=5 +GoUnblock dt=12 g=30 g_seq=15 stack=0 +GoStart dt=4 g=30 g_seq=16 +GoLabel dt=1 label_string=2 +GoBlock dt=410 reason_string=15 stack=5 +GoUnblock dt=2384 g=23 g_seq=37 stack=0 +GoStart dt=7 g=23 g_seq=38 +GoLabel dt=1 label_string=4 +GoBlock dt=119 reason_string=15 stack=5 +GoUnblock dt=58 g=25 g_seq=21 stack=0 +GoStart dt=4 g=25 g_seq=22 +GoLabel dt=1 label_string=4 +GoBlock dt=1875 reason_string=15 stack=5 +GoUnblock dt=53 g=29 g_seq=15 stack=0 +GoStart dt=3 g=29 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=133 reason_string=15 stack=5 +GoUnblock dt=51 g=25 g_seq=25 stack=0 +GoStart dt=5 g=25 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=14 reason_string=15 stack=5 +GoUnblock dt=42 g=25 g_seq=27 stack=0 +GoStart dt=3 g=25 g_seq=28 +GoLabel dt=1 label_string=4 +GoBlock dt=56 reason_string=15 stack=5 +GoUnblock dt=1741 g=24 g_seq=41 stack=0 +GoStart dt=4 g=24 g_seq=42 +GoLabel dt=3 label_string=2 +GoBlock dt=15 reason_string=15 stack=5 +GoUnblock dt=26 g=25 g_seq=31 stack=0 +GoStart dt=4 g=25 g_seq=32 +GoLabel dt=1 label_string=2 +GoBlock dt=2653 reason_string=15 stack=5 +GoUnblock dt=10 g=25 g_seq=33 stack=0 +GoStart dt=6 g=25 g_seq=34 +GoLabel dt=1 label_string=2 +GoBlock dt=151 reason_string=15 stack=5 +GoUnblock dt=37 g=25 g_seq=35 stack=0 +GoStart dt=3 g=25 g_seq=36 +GoLabel dt=1 label_string=4 +GoBlock dt=12 reason_string=15 stack=5 +GoUnblock dt=8 g=25 g_seq=37 stack=0 +GoStart dt=3 g=25 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=1197 reason_string=15 stack=5 +GoUnblock dt=38 g=22 g_seq=43 stack=0 +GoStart dt=7 g=22 g_seq=44 +GoLabel dt=1 label_string=4 +GoBlock dt=16 reason_string=15 stack=5 +ProcStop dt=28 +ProcStart dt=2728 p=7 p_seq=3 +GoUnblock dt=10 g=25 g_seq=39 stack=0 +GoStart dt=162 g=25 g_seq=40 +GoLabel dt=2 label_string=2 +GoBlock dt=36 reason_string=15 stack=5 +GoUnblock dt=10 g=25 g_seq=41 stack=0 +GoStart dt=4 g=25 g_seq=42 +GoLabel dt=1 label_string=2 +GoBlock dt=19 reason_string=15 stack=5 +GoUnblock dt=7 g=25 g_seq=43 stack=0 +GoStart dt=1 g=25 g_seq=44 +GoLabel dt=1 label_string=2 +GoUnblock dt=616 g=81 g_seq=6 stack=12 +GoBlock dt=1549 reason_string=15 stack=5 +GoStart dt=12 g=112 g_seq=5 +GoBlock dt=22 reason_string=13 stack=11 +GoStart dt=8 g=90 g_seq=4 +GCMarkAssistEnd dt=3 +HeapAlloc dt=2613 heapalloc_value=192625368 +GoStop dt=48 reason_string=16 stack=6 +GoUnblock dt=13 g=54 g_seq=35 stack=0 +GoStart dt=4 g=54 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=269 reason_string=15 stack=5 +GoUnblock dt=6 g=54 g_seq=37 stack=0 +GoStart dt=5 g=54 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=856 reason_string=15 stack=5 +GoUnblock dt=23 g=52 g_seq=61 stack=0 +GoStart dt=4 g=52 g_seq=62 +GoLabel dt=1 label_string=2 +GoBlock dt=33 reason_string=15 stack=5 +GoUnblock dt=13 g=52 g_seq=63 stack=0 +GoStart dt=2 g=52 g_seq=64 +GoLabel dt=1 label_string=2 +GoBlock dt=38 reason_string=15 stack=5 +GoUnblock dt=17 g=52 g_seq=65 stack=0 +GoStart dt=3 g=52 g_seq=66 +GoLabel dt=1 label_string=2 +GoBlock dt=37 reason_string=15 stack=5 +GoUnblock dt=11 g=52 g_seq=67 stack=0 +GoStart dt=4 g=52 g_seq=68 +GoLabel dt=1 label_string=2 +GoBlock dt=2457 reason_string=15 stack=5 +GoUnblock dt=11 g=52 g_seq=69 stack=0 +GoStart dt=4 g=52 g_seq=70 +GoLabel dt=1 label_string=2 +GoBlock dt=9 reason_string=15 stack=5 +GoStart dt=23 g=114 g_seq=4 +GCMarkAssistEnd dt=457 +HeapAlloc dt=223 heapalloc_value=194968280 +GoStop dt=6900 reason_string=16 stack=4 +GoStart dt=24 g=114 g_seq=5 +GCMarkAssistBegin dt=86 stack=3 +GoBlock dt=43 reason_string=10 stack=18 +ProcStop dt=49 +ProcStart dt=475 p=7 p_seq=4 +ProcStop dt=40 +ProcStart dt=1388 p=7 p_seq=5 +GoUnblock dt=9 g=131 g_seq=8 stack=0 +GoStart dt=169 g=131 g_seq=9 +GoSyscallBegin dt=24 p_seq=6 stack=7 +GoSyscallEnd dt=184 +GoBlock dt=11 reason_string=15 stack=2 +ProcStop dt=42 +ProcStart dt=18109 p=16 p_seq=2 +GoStart dt=176 g=91 g_seq=4 +GCMarkAssistEnd dt=8 +HeapAlloc dt=22 heapalloc_value=114837120 +HeapAlloc dt=88 heapalloc_value=114853504 +GCSweepBegin dt=145 stack=27 +EventBatch gen=3 m=169433 time=28114950897465 size=806 +ProcStatus dt=2 p=2 pstatus=1 +GoStatus dt=1 g=24 m=169433 gstatus=2 +GoBlock dt=9 reason_string=15 stack=5 +GoUnblock dt=19 g=24 g_seq=1 stack=0 +GoStart dt=5 g=24 g_seq=2 +GoLabel dt=2 label_string=2 +GoBlock dt=4044 reason_string=15 stack=5 +GoUnblock dt=17 g=24 g_seq=3 stack=0 +GoStart dt=4 g=24 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=4262 reason_string=15 stack=5 +GoUnblock dt=19 g=28 g_seq=3 stack=0 +GoStart dt=4 g=28 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=461 reason_string=15 stack=5 +GoUnblock dt=4544 g=72 g_seq=15 stack=0 +GoStart dt=9 g=72 g_seq=16 +GoLabel dt=3 label_string=4 +GoBlock dt=32 reason_string=15 stack=5 +GoUnblock dt=9 g=72 g_seq=17 stack=0 +GoStart dt=2 g=72 g_seq=18 +GoLabel dt=2 label_string=2 +GoBlock dt=13 reason_string=15 stack=5 +GoUnblock dt=3 g=72 g_seq=19 stack=0 +GoStart dt=1 g=72 g_seq=20 +GoLabel dt=1 label_string=2 +GoBlock dt=237 reason_string=15 stack=5 +GoUnblock dt=8 g=72 g_seq=21 stack=0 +GoStart dt=3 g=72 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=151 reason_string=15 stack=5 +GoUnblock dt=11 g=72 g_seq=23 stack=0 +GoStart dt=6 g=72 g_seq=24 +GoLabel dt=1 label_string=2 +GoBlock dt=3418 reason_string=15 stack=5 +ProcStop dt=1573 +ProcStart dt=17 p=2 p_seq=1 +ProcStop dt=1102 +ProcStart dt=21668 p=19 p_seq=4 +GoUnblock dt=16 g=51 g_seq=47 stack=0 +GoStart dt=7 g=51 g_seq=48 +GoLabel dt=1 label_string=2 +GoBlock dt=60 reason_string=15 stack=5 +GoUnblock dt=6 g=51 g_seq=49 stack=0 +GoStart dt=1 g=51 g_seq=50 +GoLabel dt=3 label_string=2 +GoBlock dt=5166 reason_string=15 stack=5 +GoStart dt=18 g=106 g_seq=5 +GCMarkAssistEnd dt=10 +HeapAlloc dt=56 heapalloc_value=193452760 +GCMarkAssistBegin dt=116 stack=3 +GCMarkAssistEnd dt=58 +HeapAlloc dt=47 heapalloc_value=193714904 +GoStop dt=54 reason_string=16 stack=6 +GoUnblock dt=18 g=54 g_seq=41 stack=0 +GoStart dt=4 g=54 g_seq=42 +GoLabel dt=2 label_string=2 +GoUnblock dt=16 g=105 g_seq=11 stack=12 +GoBlock dt=21 reason_string=15 stack=5 +GoStart dt=8 g=105 g_seq=12 +GCMarkAssistEnd dt=7 +HeapAlloc dt=33 heapalloc_value=193919704 +GCMarkAssistBegin dt=13 stack=3 +GCMarkAssistEnd dt=91 +HeapAlloc dt=173 heapalloc_value=194378456 +GCMarkAssistBegin dt=26 stack=3 +GoBlock dt=37 reason_string=13 stack=11 +GoStart dt=33 g=104 g_seq=2 +GCMarkAssistEnd dt=5 +HeapAlloc dt=81 heapalloc_value=194673368 +GoStop dt=2248 reason_string=16 stack=6 +GoStart dt=2855 g=104 g_seq=3 +GCMarkAssistBegin dt=16 stack=3 +GoBlock dt=27 reason_string=10 stack=18 +GoStart dt=16 g=103 g_seq=5 +GCMarkAssistEnd dt=6 +HeapAlloc dt=6180 heapalloc_value=196655568 +GoStop dt=14 reason_string=16 stack=6 +GoStart dt=146 g=102 g_seq=5 +GCMarkAssistBegin dt=10 stack=3 +HeapAlloc dt=38 heapalloc_value=196663760 +GoBlock dt=16 reason_string=10 stack=18 +ProcStop dt=41 +ProcStart dt=1317 p=19 p_seq=5 +ProcStop dt=24 +ProcStart dt=2117 p=0 p_seq=5 +GoStart dt=5190 g=115 g_seq=10 +GCMarkAssistEnd dt=6 +GCSweepBegin dt=22 stack=27 +GCSweepEnd dt=727 swept_value=71303168 reclaimed_value=1302272 +HeapAlloc dt=37 heapalloc_value=103898784 +HeapAlloc dt=200 heapalloc_value=103947936 +HeapAlloc dt=63 heapalloc_value=103960224 +HeapAlloc dt=27 heapalloc_value=103997088 +HeapAlloc dt=65 heapalloc_value=104103584 +HeapAlloc dt=87 heapalloc_value=104132512 +HeapAlloc dt=63 heapalloc_value=104255392 +HeapAlloc dt=87 heapalloc_value=104267680 +HeapAlloc dt=73 heapalloc_value=104379424 +HeapAlloc dt=79 heapalloc_value=104494112 +GCSweepBegin dt=40 stack=28 +GCSweepEnd dt=7 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=8 heapalloc_value=104526880 +HeapAlloc dt=27 heapalloc_value=104589088 +HeapAlloc dt=42 heapalloc_value=104711968 +HeapAlloc dt=83 heapalloc_value=104821280 +GCSweepBegin dt=21 stack=28 +GCSweepEnd dt=4 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=2 heapalloc_value=104854048 +HeapAlloc dt=105 heapalloc_value=105064992 +GCSweepBegin dt=94 stack=28 +GCSweepEnd dt=9 swept_value=8192 reclaimed_value=8192 +HeapAlloc dt=4 heapalloc_value=105250976 +GCSweepBegin dt=29 stack=28 +GCSweepEnd dt=10 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=4 heapalloc_value=105447584 +HeapAlloc dt=30 heapalloc_value=105476256 +HeapAlloc dt=57 heapalloc_value=105566368 +GCSweepBegin dt=74 stack=28 +GCSweepEnd dt=5 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=105741216 +HeapAlloc dt=77 heapalloc_value=105921440 +HeapAlloc dt=76 heapalloc_value=106143904 +HeapAlloc dt=50 heapalloc_value=106274976 +HeapAlloc dt=113 heapalloc_value=106633504 +HeapAlloc dt=110 heapalloc_value=107036320 +HeapAlloc dt=95 heapalloc_value=107351072 +HeapAlloc dt=80 heapalloc_value=107702048 +GCSweepBegin dt=78 stack=28 +GCSweepEnd dt=6 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=2 heapalloc_value=107835936 +HeapAlloc dt=39 heapalloc_value=107904288 +HeapAlloc dt=82 heapalloc_value=108390432 +HeapAlloc dt=230 heapalloc_value=108955808 +HeapAlloc dt=126 heapalloc_value=109421344 +GCSweepBegin dt=131 stack=28 +GCSweepEnd dt=5 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=3 heapalloc_value=109929504 +GCSweepBegin dt=29 stack=28 +GCSweepEnd dt=4 swept_value=8192 reclaimed_value=8192 +HeapAlloc dt=3 heapalloc_value=110038816 +HeapAlloc dt=28 heapalloc_value=110109472 +HeapAlloc dt=93 heapalloc_value=110412672 +HeapAlloc dt=33 heapalloc_value=110547840 +HeapAlloc dt=123 heapalloc_value=111070848 +GCSweepBegin dt=155 stack=28 +GCSweepEnd dt=10 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=3 heapalloc_value=111648640 +GCSweepBegin dt=61 stack=28 +GCSweepEnd dt=8 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=3 heapalloc_value=111996800 +GCSweepBegin dt=37 stack=28 +GCSweepEnd dt=5 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=8 heapalloc_value=112149760 +HeapAlloc dt=32 heapalloc_value=112342272 +GCSweepBegin dt=75 stack=28 +GCSweepEnd dt=7 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=5 heapalloc_value=112601856 +HeapAlloc dt=61 heapalloc_value=112923264 +HeapAlloc dt=90 heapalloc_value=113262720 +HeapAlloc dt=88 heapalloc_value=113522304 +HeapAlloc dt=119 heapalloc_value=113967488 +HeapAlloc dt=59 heapalloc_value=114201216 +GCSweepBegin dt=130 stack=27 +EventBatch gen=3 m=169431 time=28114950897743 size=407 +ProcStatus dt=2 p=11 pstatus=1 +GoStatus dt=4 g=51 m=169431 gstatus=2 +GoBlock dt=6 reason_string=15 stack=5 +GoUnblock dt=13 g=51 g_seq=1 stack=0 +GoStart dt=6 g=51 g_seq=2 +GoLabel dt=1 label_string=2 +GoBlock dt=4143 reason_string=15 stack=5 +GoStatus dt=1425 g=28 m=18446744073709551615 gstatus=4 +GoUnblock dt=2 g=28 g_seq=1 stack=0 +GoStart dt=7 g=28 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=1758 reason_string=15 stack=5 +GoUnblock dt=3904 g=25 g_seq=9 stack=0 +GoStart dt=9 g=25 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=41 reason_string=15 stack=5 +ProcStop dt=1189 +ProcStart dt=16 p=11 p_seq=1 +GoUnblock dt=1157 g=57 g_seq=21 stack=0 +GoStart dt=6 g=57 g_seq=22 +GoLabel dt=1 label_string=4 +GoBlock dt=25 reason_string=15 stack=5 +GoUnblock dt=1614 g=52 g_seq=13 stack=0 +GoStart dt=11 g=52 g_seq=14 +GoLabel dt=4 label_string=4 +GoBlock dt=86 reason_string=15 stack=5 +GoUnblock dt=4771 g=22 g_seq=11 stack=0 +GoStart dt=12 g=22 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=1413 reason_string=15 stack=5 +GoUnblock dt=10 g=22 g_seq=13 stack=0 +GoStart dt=4 g=22 g_seq=14 +GoLabel dt=1 label_string=2 +GoBlock dt=39 reason_string=15 stack=5 +ProcStop dt=67 +ProcStart dt=2286 p=11 p_seq=2 +ProcStop dt=95 +ProcStart dt=53 p=0 p_seq=2 +GoUnblock dt=9 g=57 g_seq=33 stack=0 +GoStart dt=8 g=57 g_seq=34 +GoLabel dt=1 label_string=4 +GoBlock dt=37 reason_string=15 stack=5 +GoUnblock dt=20 g=22 g_seq=23 stack=0 +GoStart dt=3 g=22 g_seq=24 +GoLabel dt=1 label_string=2 +GoBlock dt=1036 reason_string=15 stack=5 +GoUnblock dt=11 g=22 g_seq=25 stack=0 +GoStart dt=6 g=22 g_seq=26 +GoLabel dt=1 label_string=2 +GoBlock dt=2130 reason_string=15 stack=5 +GoUnblock dt=11 g=22 g_seq=27 stack=0 +GoStart dt=7 g=22 g_seq=28 +GoLabel dt=2 label_string=2 +GoBlock dt=1227 reason_string=15 stack=5 +GoUnblock dt=12 g=22 g_seq=29 stack=0 +GoStart dt=6 g=22 g_seq=30 +GoLabel dt=1 label_string=2 +GoBlock dt=31 reason_string=15 stack=5 +GoUnblock dt=7 g=22 g_seq=31 stack=0 +GoStart dt=2 g=22 g_seq=32 +GoLabel dt=1 label_string=2 +GoBlock dt=2282 reason_string=15 stack=5 +GoUnblock dt=71 g=29 g_seq=33 stack=0 +GoStart dt=4 g=29 g_seq=34 +GoLabel dt=1 label_string=4 +GoBlock dt=1234 reason_string=15 stack=5 +GoUnblock dt=8 g=29 g_seq=35 stack=0 +GoStart dt=8 g=29 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=18 reason_string=15 stack=5 +ProcStop dt=49 +ProcStart dt=10623 p=11 p_seq=5 +ProcStop dt=54 +ProcStart dt=686 p=11 p_seq=6 +GoStart dt=185 g=127 g_seq=5 +GCMarkAssistBegin dt=71 stack=3 +GoStop dt=67 reason_string=20 stack=9 +GoUnblock dt=15 g=53 g_seq=47 stack=0 +GoStart dt=3 g=53 g_seq=48 +GoLabel dt=1 label_string=2 +GoUnblock dt=661 g=121 g_seq=10 stack=12 +GoUnblock dt=7 g=88 g_seq=5 stack=12 +GoUnblock dt=8 g=87 g_seq=4 stack=12 +GoUnblock dt=2751 g=94 g_seq=10 stack=12 +GoUnblock dt=8 g=106 g_seq=7 stack=12 +GoUnblock dt=8 g=98 g_seq=9 stack=12 +GoBlock dt=18 reason_string=15 stack=5 +GoStart dt=17 g=87 g_seq=5 +GCMarkAssistEnd dt=5 +HeapAlloc dt=202 heapalloc_value=194796248 +GoStop dt=7327 reason_string=16 stack=6 +GoStart dt=68 g=84 g_seq=8 +GCMarkAssistBegin dt=16 stack=3 +GoBlock dt=29 reason_string=13 stack=11 +ProcStop dt=88 +EventBatch gen=3 m=169428 time=28114950899204 size=756 +ProcStatus dt=2 p=31 pstatus=1 +GoStatus dt=5 g=104 m=169428 gstatus=2 +GCMarkAssistActive dt=1 g=104 +GCMarkAssistEnd dt=2 +HeapAlloc dt=37 heapalloc_value=191110592 +GCMarkAssistBegin dt=21 stack=3 +GoBlock dt=2670 reason_string=13 stack=11 +GoStatus dt=1400 g=22 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=22 g_seq=1 stack=0 +GoStart dt=7 g=22 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=43 reason_string=15 stack=5 +GoUnblock dt=2567 g=70 g_seq=3 stack=0 +GoStart dt=9 g=70 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=329 reason_string=15 stack=5 +GoUnblock dt=97 g=70 g_seq=5 stack=0 +GoStart dt=5 g=70 g_seq=6 +GoLabel dt=3 label_string=2 +GoUnblock dt=1728 g=84 g_seq=3 stack=12 +GoBlock dt=3527 reason_string=15 stack=5 +GoStart dt=4132 g=114 g_seq=2 +GoStatus dt=28 g=115 m=18446744073709551615 gstatus=4 +GoUnblock dt=8 g=115 g_seq=1 stack=10 +GCMarkAssistBegin dt=18 stack=3 +GoBlock dt=196 reason_string=13 stack=11 +GoStart dt=14 g=115 g_seq=2 +GoStatus dt=18 g=102 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=102 g_seq=1 stack=10 +GCMarkAssistBegin dt=13 stack=3 +GoBlock dt=371 reason_string=13 stack=11 +GoUnblock dt=9 g=30 g_seq=11 stack=0 +GoStart dt=6 g=30 g_seq=12 +GoLabel dt=1 label_string=2 +GoBlock dt=5520 reason_string=15 stack=5 +GoUnblock dt=8 g=30 g_seq=13 stack=0 +GoStart dt=4 g=30 g_seq=14 +GoLabel dt=1 label_string=2 +GoBlock dt=28 reason_string=15 stack=5 +GoUnblock dt=10 g=57 g_seq=37 stack=0 +GoStart dt=3 g=57 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=157 reason_string=15 stack=5 +GoUnblock dt=7 g=57 g_seq=39 stack=0 +GoStart dt=4 g=57 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=140 reason_string=15 stack=5 +GoUnblock dt=10 g=53 g_seq=25 stack=0 +GoStart dt=3 g=53 g_seq=26 +GoLabel dt=1 label_string=2 +GoBlock dt=90 reason_string=15 stack=5 +GoUnblock dt=62 g=53 g_seq=27 stack=0 +GoStart dt=4 g=53 g_seq=28 +GoLabel dt=1 label_string=4 +GoBlock dt=11 reason_string=15 stack=5 +GoUnblock dt=46 g=53 g_seq=29 stack=0 +GoStart dt=7 g=53 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=51 reason_string=15 stack=5 +ProcStop dt=2236 +ProcStart dt=966 p=35 p_seq=2 +GoStart dt=19 g=81 g_seq=5 +GCMarkAssistEnd dt=7 +HeapAlloc dt=67 heapalloc_value=192133920 +GCMarkAssistBegin dt=46 stack=3 +GoBlock dt=32 reason_string=13 stack=11 +ProcStop dt=57 +ProcStart dt=15 p=35 p_seq=3 +GoUnblock dt=2 g=69 g_seq=23 stack=0 +GoStart dt=2 g=69 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=224 reason_string=15 stack=5 +GoUnblock dt=52 g=69 g_seq=25 stack=0 +GoStart dt=3 g=69 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=289 reason_string=15 stack=5 +GoStart dt=23 g=118 g_seq=2 +GCMarkAssistEnd dt=7 +HeapAlloc dt=21 heapalloc_value=192207648 +GCMarkAssistBegin dt=103 stack=3 +GoBlock dt=18 reason_string=13 stack=11 +GoUnblock dt=48 g=29 g_seq=13 stack=0 +GoStart dt=1 g=29 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=19 reason_string=15 stack=5 +GoUnblock dt=44 g=25 g_seq=23 stack=0 +GoStart dt=6 g=25 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=144 reason_string=15 stack=5 +GoUnblock dt=49 g=29 g_seq=17 stack=0 +GoStart dt=1 g=29 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=777 reason_string=15 stack=5 +GoUnblock dt=56 g=52 g_seq=31 stack=0 +GoStart dt=3 g=52 g_seq=32 +GoLabel dt=1 label_string=4 +GoBlock dt=21 reason_string=15 stack=5 +GoUnblock dt=27 g=51 g_seq=33 stack=0 +GoStart dt=5 g=51 g_seq=34 +GoLabel dt=1 label_string=2 +GoBlock dt=12 reason_string=15 stack=5 +GoUnblock dt=13 g=51 g_seq=35 stack=0 +GoStart dt=4 g=51 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=226 reason_string=15 stack=5 +GoUnblock dt=7 g=51 g_seq=37 stack=0 +GoStart dt=4 g=51 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=3928 reason_string=15 stack=5 +GoUnblock dt=14 g=51 g_seq=39 stack=0 +GoStart dt=3 g=51 g_seq=40 +GoLabel dt=3 label_string=2 +GoBlock dt=214 reason_string=15 stack=5 +GoUnblock dt=5 g=51 g_seq=41 stack=0 +GoStart dt=1 g=51 g_seq=42 +GoLabel dt=1 label_string=2 +GoBlock dt=305 reason_string=15 stack=5 +GoUnblock dt=8 g=51 g_seq=43 stack=0 +GoStart dt=5 g=51 g_seq=44 +GoLabel dt=1 label_string=2 +GoBlock dt=9 reason_string=15 stack=5 +ProcStop dt=47 +ProcStart dt=5058 p=35 p_seq=4 +GoUnblock dt=20 g=52 g_seq=51 stack=0 +GoStart dt=188 g=52 g_seq=52 +GoLabel dt=1 label_string=2 +GoBlock dt=33 reason_string=15 stack=5 +GoUnblock dt=9 g=52 g_seq=53 stack=0 +GoStart dt=4 g=52 g_seq=54 +GoLabel dt=1 label_string=2 +GoBlock dt=12 reason_string=15 stack=5 +GoStart dt=14 g=126 g_seq=3 +GCMarkAssistEnd dt=7 +HeapAlloc dt=2068 heapalloc_value=192592600 +GoStop dt=31 reason_string=16 stack=4 +GoUnblock dt=10 g=30 g_seq=39 stack=0 +GoStart dt=4 g=30 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=54 reason_string=15 stack=5 +GoStart dt=708 g=121 g_seq=9 +GoBlock dt=49 reason_string=13 stack=11 +GoStart dt=18 g=90 g_seq=5 +GCMarkAssistBegin dt=12 stack=3 +GoBlock dt=39 reason_string=13 stack=11 +GoUnblock dt=15 g=71 g_seq=31 stack=0 +GoStart dt=4 g=71 g_seq=32 +GoLabel dt=2 label_string=2 +GoBlock dt=1101 reason_string=15 stack=5 +GoUnblock dt=13 g=71 g_seq=33 stack=0 +GoStart dt=4 g=71 g_seq=34 +GoLabel dt=1 label_string=2 +GoBlock dt=27 reason_string=15 stack=5 +GoUnblock dt=18 g=14 g_seq=54 stack=0 +GoStart dt=4 g=14 g_seq=55 +GoLabel dt=2 label_string=2 +GoUnblock dt=2171 g=94 g_seq=8 stack=12 +GoBlock dt=28 reason_string=15 stack=5 +GoStart dt=11 g=94 g_seq=9 +GCMarkAssistEnd dt=6 +HeapAlloc dt=42 heapalloc_value=193665752 +GCMarkAssistBegin dt=100 stack=3 +GoBlock dt=30 reason_string=13 stack=11 +GoStart dt=13 g=106 g_seq=6 +HeapAlloc dt=99 heapalloc_value=193977048 +GCMarkAssistBegin dt=21 stack=3 +GoBlock dt=30 reason_string=13 stack=11 +GoStart dt=16 g=92 g_seq=4 +GCMarkAssistEnd dt=6 +HeapAlloc dt=884 heapalloc_value=195205848 +GoStop dt=3270 reason_string=16 stack=4 +GoStart dt=29 g=97 g_seq=6 +GCMarkAssistEnd dt=6 +HeapAlloc dt=42 heapalloc_value=195795408 +GCMarkAssistBegin dt=3026 stack=3 +GoBlock dt=85 reason_string=10 stack=18 +ProcStop dt=99 +EventBatch gen=3 m=169426 time=28114950897488 size=116 +ProcStatus dt=1 p=32 pstatus=1 +GoStatus dt=2 g=90 m=169426 gstatus=2 +GCMarkAssistActive dt=1 g=90 +GCMarkAssistEnd dt=3 +HeapAlloc dt=47 heapalloc_value=190627264 +GCMarkAssistBegin dt=51 stack=3 +GoBlock dt=2393 reason_string=13 stack=11 +GoStart dt=1449 g=125 g_seq=2 +GoStatus dt=26 g=127 m=18446744073709551615 gstatus=4 +GoUnblock dt=16 g=127 g_seq=1 stack=10 +GCMarkAssistBegin dt=17 stack=3 +GoStop dt=6909 reason_string=20 stack=9 +GoStart dt=20 g=125 g_seq=3 +GoBlock dt=2101 reason_string=13 stack=11 +GoUnblock dt=2575 g=71 g_seq=11 stack=0 +GoStart dt=8 g=71 g_seq=12 +GoLabel dt=3 label_string=4 +GoBlock dt=44 reason_string=15 stack=5 +GoStart dt=20 g=82 g_seq=7 +GoBlock dt=367 reason_string=13 stack=11 +GoUnblock dt=11 g=22 g_seq=9 stack=0 +GoStart dt=4 g=22 g_seq=10 +GoLabel dt=1 label_string=2 +GoBlock dt=3492 reason_string=15 stack=5 +ProcStop dt=9 +EventBatch gen=3 m=169425 time=28114950900302 size=349 +ProcStatus dt=2 p=10 pstatus=1 +GoStatus dt=2 g=70 m=169425 gstatus=2 +GoBlock dt=8 reason_string=15 stack=5 +GoUnblock dt=15 g=70 g_seq=1 stack=0 +GoStart dt=5 g=70 g_seq=2 +GoLabel dt=1 label_string=2 +GoBlock dt=5604 reason_string=15 stack=5 +GoUnblock dt=33 g=29 g_seq=3 stack=0 +GoStart dt=5 g=29 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=1191 reason_string=15 stack=5 +GoUnblock dt=9 g=29 g_seq=5 stack=0 +GoStart dt=5 g=29 g_seq=6 +GoLabel dt=2 label_string=2 +GoBlock dt=1935 reason_string=15 stack=5 +GoUnblock dt=15 g=51 g_seq=11 stack=0 +GoStart dt=5 g=51 g_seq=12 +GoLabel dt=1 label_string=2 +GoBlock dt=307 reason_string=15 stack=5 +ProcStop dt=4189 +ProcStart dt=15 p=10 p_seq=1 +GoUnblock dt=10 g=69 g_seq=17 stack=0 +GoStart dt=4 g=69 g_seq=18 +GoLabel dt=1 label_string=2 +GoUnblock dt=780 g=93 g_seq=3 stack=12 +GoBlock dt=6076 reason_string=15 stack=5 +GoUnblock dt=11 g=69 g_seq=19 stack=0 +GoStart dt=4 g=69 g_seq=20 +GoLabel dt=3 label_string=2 +GoUnblock dt=93 g=98 g_seq=5 stack=12 +GoBlock dt=5034 reason_string=15 stack=5 +GoUnblock dt=14 g=58 g_seq=25 stack=0 +GoStart dt=5 g=58 g_seq=26 +GoLabel dt=2 label_string=2 +GoBlock dt=1253 reason_string=15 stack=5 +GoUnblock dt=6 g=58 g_seq=27 stack=0 +GoStart dt=4 g=58 g_seq=28 +GoLabel dt=1 label_string=2 +GoBlock dt=1031 reason_string=15 stack=5 +GoUnblock dt=6 g=58 g_seq=29 stack=0 +GoStart dt=4 g=58 g_seq=30 +GoLabel dt=1 label_string=2 +GoBlock dt=17 reason_string=15 stack=5 +GoUnblock dt=24 g=52 g_seq=33 stack=0 +GoStart dt=6 g=52 g_seq=34 +GoLabel dt=1 label_string=2 +GoBlock dt=321 reason_string=15 stack=5 +GoUnblock dt=75 g=29 g_seq=27 stack=0 +GoStart dt=4 g=29 g_seq=28 +GoLabel dt=1 label_string=4 +GoBlock dt=248 reason_string=15 stack=5 +GoUnblock dt=61 g=57 g_seq=47 stack=0 +GoStart dt=4 g=57 g_seq=48 +GoLabel dt=1 label_string=4 +GoBlock dt=794 reason_string=15 stack=5 +ProcStop dt=41 +ProcStart dt=15678 p=21 p_seq=3 +GoStart dt=22 g=88 g_seq=6 +GCMarkAssistEnd dt=9 +HeapAlloc dt=84 heapalloc_value=194730712 +GoStop dt=2177 reason_string=16 stack=6 +GoStart dt=2495 g=88 g_seq=7 +GCMarkAssistBegin dt=19 stack=3 +GoBlock dt=37 reason_string=10 stack=18 +GoStart dt=15 g=126 g_seq=6 +GCMarkAssistEnd dt=5 +HeapAlloc dt=27 heapalloc_value=196114896 +GCMarkAssistBegin dt=18 stack=3 +GoBlock dt=30 reason_string=10 stack=18 +GoStart dt=15 g=98 g_seq=10 +GCMarkAssistEnd dt=6 +HeapAlloc dt=48 heapalloc_value=196155856 +GoStop dt=6168 reason_string=16 stack=6 +GoStart dt=156 g=98 g_seq=11 +GCMarkAssistBegin dt=9 stack=3 +GoBlock dt=27 reason_string=10 stack=18 +GoStart dt=27 g=94 g_seq=12 +GCMarkAssistBegin dt=13 stack=3 +GoBlock dt=35 reason_string=10 stack=18 +ProcStop dt=55 +ProcStart dt=14725 p=13 p_seq=1 +GoStart dt=171 g=112 g_seq=9 +GCMarkAssistEnd dt=6 +GCSweepBegin dt=222 stack=27 +EventBatch gen=3 m=169424 time=28114950894869 size=176 +ProcStatus dt=1 p=23 pstatus=3 +GoStatus dt=2 g=131 m=169424 gstatus=3 +GoSyscallEnd dt=1 +GoBlock dt=64 reason_string=15 stack=2 +GoUnblock dt=2260 g=67 g_seq=1 stack=0 +GoStart dt=6 g=67 g_seq=2 +GoLabel dt=2 label_string=4 +GoBlock dt=530 reason_string=15 stack=5 +GoUnblock dt=12 g=69 g_seq=3 stack=0 +GoStart dt=5 g=69 g_seq=4 +GoLabel dt=1 label_string=2 +GoUnblock dt=2455 g=90 g_seq=1 stack=12 +GoBlock dt=20 reason_string=15 stack=5 +GoUnblock dt=16 g=69 g_seq=5 stack=0 +GoStart dt=7 g=69 g_seq=6 +GoLabel dt=1 label_string=2 +GoUnblock dt=493 g=120 g_seq=2 stack=12 +GoBlock dt=1777 reason_string=15 stack=5 +GoUnblock dt=832 g=69 g_seq=7 stack=0 +GoStart dt=9 g=69 g_seq=8 +GoLabel dt=2 label_string=2 +GoBlock dt=5425 reason_string=15 stack=5 +GoUnblock dt=15 g=69 g_seq=9 stack=0 +GoStart dt=4 g=69 g_seq=10 +GoLabel dt=1 label_string=2 +GoBlock dt=1370 reason_string=15 stack=5 +ProcStop dt=2533 +ProcStart dt=11 p=23 p_seq=1 +GoUnblock dt=3354 g=54 g_seq=17 stack=0 +GoStart dt=7 g=54 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=29 reason_string=15 stack=5 +GoUnblock dt=25 g=54 g_seq=19 stack=0 +GoStart dt=5 g=54 g_seq=20 +GoLabel dt=2 label_string=2 +GoBlock dt=232 reason_string=15 stack=5 +GoUnblock dt=11 g=54 g_seq=21 stack=0 +GoStart dt=7 g=54 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=293 reason_string=15 stack=5 +ProcStop dt=11 +EventBatch gen=3 m=169423 time=28114950894865 size=525 +ProcStatus dt=2 p=30 pstatus=1 +GoStatus dt=4 g=87 m=169423 gstatus=2 +GCMarkAssistActive dt=1 g=87 +GCMarkAssistEnd dt=2 +HeapAlloc dt=98 heapalloc_value=189963712 +GoStop dt=56 reason_string=16 stack=6 +GoUnblock dt=20 g=131 g_seq=1 stack=0 +GoStart dt=7 g=131 g_seq=2 +GoSyscallBegin dt=39 p_seq=1 stack=7 +GoSyscallEnd dt=304 +GoSyscallBegin dt=19 p_seq=2 stack=7 +GoSyscallEnd dt=59 +GoSyscallBegin dt=15 p_seq=3 stack=7 +GoSyscallEnd dt=52 +GoSyscallBegin dt=11 p_seq=4 stack=7 +GoSyscallEnd dt=50 +GoSyscallBegin dt=8 p_seq=5 stack=7 +GoSyscallEnd dt=48 +GoSyscallBegin dt=10 p_seq=6 stack=7 +GoSyscallEnd dt=54 +GoSyscallBegin dt=13 p_seq=7 stack=7 +GoSyscallEnd dt=51 +GoSyscallBegin dt=12 p_seq=8 stack=7 +GoSyscallEnd dt=49 +GoSyscallBegin dt=16 p_seq=9 stack=7 +GoSyscallEnd dt=245 +GoSyscallBegin dt=12 p_seq=10 stack=7 +GoSyscallEnd dt=49 +GoSyscallBegin dt=10 p_seq=11 stack=7 +GoSyscallEnd dt=49 +GoSyscallBegin dt=10 p_seq=12 stack=7 +GoSyscallEnd dt=48 +GoSyscallBegin dt=6 p_seq=13 stack=7 +GoSyscallEnd dt=52 +GoStop dt=24 reason_string=16 stack=8 +GoUnblock dt=9 g=14 g_seq=1 stack=0 +GoStart dt=5 g=14 g_seq=2 +GoLabel dt=1 label_string=2 +GoUnblock dt=2948 g=107 g_seq=1 stack=12 +GoBlock dt=2891 reason_string=15 stack=5 +GoUnblock dt=11 g=14 g_seq=3 stack=0 +GoStart dt=5 g=14 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=1138 reason_string=15 stack=5 +GoUnblock dt=22 g=51 g_seq=5 stack=0 +GoStart dt=5 g=51 g_seq=6 +GoLabel dt=1 label_string=2 +GoUnblock dt=451 g=82 g_seq=3 stack=12 +GoBlock dt=460 reason_string=15 stack=5 +GoUnblock dt=4052 g=54 g_seq=5 stack=0 +GoStart dt=11 g=54 g_seq=6 +GoLabel dt=1 label_string=4 +GoBlock dt=72 reason_string=15 stack=5 +GoUnblock dt=1333 g=57 g_seq=15 stack=0 +GoStart dt=8 g=57 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=283 reason_string=15 stack=5 +GoUnblock dt=1185 g=57 g_seq=19 stack=0 +GoStart dt=7 g=57 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=134 reason_string=15 stack=5 +GoUnblock dt=1144 g=53 g_seq=11 stack=0 +GoStart dt=6 g=53 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=372 reason_string=15 stack=5 +GoUnblock dt=16 g=53 g_seq=13 stack=0 +GoStart dt=7 g=53 g_seq=14 +GoLabel dt=1 label_string=2 +GoBlock dt=8581 reason_string=15 stack=5 +ProcStop dt=76 +ProcStart dt=22 p=30 p_seq=14 +GoUnblock dt=3 g=72 g_seq=31 stack=0 +GoStart dt=7 g=72 g_seq=32 +GoLabel dt=1 label_string=4 +GoBlock dt=46 reason_string=15 stack=5 +GoUnblock dt=63 g=23 g_seq=31 stack=0 +GoStart dt=7 g=23 g_seq=32 +GoLabel dt=1 label_string=4 +GoBlock dt=34 reason_string=15 stack=5 +GoUnblock dt=14 g=23 g_seq=33 stack=0 +GoStart dt=4 g=23 g_seq=34 +GoLabel dt=2 label_string=2 +GoBlock dt=47 reason_string=15 stack=5 +GoUnblock dt=74 g=53 g_seq=19 stack=0 +GoStart dt=6 g=53 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=154 reason_string=15 stack=5 +GoStatus dt=91 g=56 m=18446744073709551615 gstatus=4 +GoUnblock dt=2 g=56 g_seq=1 stack=0 +GoStart dt=43 g=56 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=45 reason_string=15 stack=5 +GoUnblock dt=65 g=53 g_seq=23 stack=0 +GoStart dt=8 g=53 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=16 reason_string=15 stack=5 +ProcStop dt=2526 +ProcStart dt=208 p=30 p_seq=15 +GoUnblock dt=8 g=53 g_seq=37 stack=0 +GoStart dt=5 g=53 g_seq=38 +GoLabel dt=1 label_string=4 +GoBlock dt=694 reason_string=15 stack=5 +GoUnblock dt=14 g=53 g_seq=39 stack=0 +GoStart dt=4 g=53 g_seq=40 +GoLabel dt=3 label_string=2 +GoBlock dt=336 reason_string=15 stack=5 +GoUnblock dt=52 g=53 g_seq=41 stack=0 +GoStart dt=4 g=53 g_seq=42 +GoLabel dt=1 label_string=4 +GoUnblock dt=449 g=118 g_seq=1 stack=12 +GoBlock dt=17 reason_string=15 stack=5 +GoUnblock dt=65 g=24 g_seq=31 stack=0 +GoStart dt=6 g=24 g_seq=32 +GoLabel dt=2 label_string=4 +GoBlock dt=56 reason_string=15 stack=5 +GoUnblock dt=54 g=24 g_seq=33 stack=0 +GoStart dt=5 g=24 g_seq=34 +GoLabel dt=1 label_string=4 +GoBlock dt=489 reason_string=15 stack=5 +GoUnblock dt=9 g=24 g_seq=35 stack=0 +GoStart dt=4 g=24 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=1307 reason_string=15 stack=5 +ProcStop dt=84 +ProcStart dt=23944 p=15 p_seq=1 +GoStart dt=174 g=108 g_seq=3 +GCMarkAssistBegin dt=25 stack=3 +GoBlock dt=59 reason_string=10 stack=18 +ProcStop dt=71 +EventBatch gen=3 m=169421 time=28114950900230 size=330 +ProcStatus dt=1 p=33 pstatus=1 +GoStatus dt=5 g=81 m=169421 gstatus=2 +GCMarkAssistActive dt=3 g=81 +GoBlock dt=7 reason_string=13 stack=11 +GoStatus dt=1543 g=57 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=57 g_seq=1 stack=0 +GoStart dt=10 g=57 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=123 reason_string=15 stack=5 +GoStatus dt=1345 g=58 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=58 g_seq=1 stack=0 +GoStart dt=5 g=58 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=154 reason_string=15 stack=5 +GoUnblock dt=5938 g=54 g_seq=7 stack=0 +GoStart dt=7 g=54 g_seq=8 +GoLabel dt=1 label_string=4 +GoBlock dt=93 reason_string=15 stack=5 +GoStart dt=1331 g=97 g_seq=2 +GoStatus dt=26 g=93 m=18446744073709551615 gstatus=4 +GoUnblock dt=6 g=93 g_seq=1 stack=10 +GCMarkAssistBegin dt=18 stack=3 +GCMarkAssistEnd dt=1894 +HeapAlloc dt=57 heapalloc_value=191872448 +GCMarkAssistBegin dt=26 stack=3 +GoBlock dt=46 reason_string=13 stack=11 +GoUnblock dt=2442 g=52 g_seq=19 stack=0 +GoStart dt=14 g=52 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=767 reason_string=15 stack=5 +ProcStop dt=2248 +ProcStart dt=24 p=33 p_seq=1 +GoUnblock dt=8 g=72 g_seq=27 stack=0 +GoStart dt=7 g=72 g_seq=28 +GoLabel dt=1 label_string=4 +GoUnblock dt=172 g=119 g_seq=3 stack=12 +GoBlock dt=1629 reason_string=15 stack=5 +GoUnblock dt=71 g=28 g_seq=9 stack=0 +GoStart dt=7 g=28 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=276 reason_string=15 stack=5 +GoUnblock dt=72 g=28 g_seq=11 stack=0 +GoStart dt=4 g=28 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=2016 reason_string=15 stack=5 +GoUnblock dt=16 g=28 g_seq=13 stack=0 +GoStart dt=7 g=28 g_seq=14 +GoLabel dt=1 label_string=2 +GoBlock dt=6712 reason_string=15 stack=5 +ProcStop dt=63 +ProcStart dt=20808 p=14 p_seq=1 +GoStart dt=205 g=89 g_seq=7 +GCMarkAssistEnd dt=10 +HeapAlloc dt=64 heapalloc_value=196245968 +GoStop dt=6073 reason_string=16 stack=6 +GoStart dt=21 g=89 g_seq=8 +GCMarkAssistBegin dt=15 stack=3 +GoBlock dt=38 reason_string=10 stack=18 +ProcStop dt=129 +ProcStart dt=13557 p=11 p_seq=7 +GoStart dt=202 g=116 g_seq=12 +GCMarkAssistEnd dt=10 +GCSweepBegin dt=25 stack=28 +GCSweepEnd dt=12 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=114760576 +GCSweepBegin dt=70 stack=28 +GCSweepEnd dt=5 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=1 heapalloc_value=114785152 +GCSweepBegin dt=353 stack=27 +EventBatch gen=3 m=169420 time=28114950896337 size=112 +ProcStatus dt=2 p=17 pstatus=1 +GoStatus dt=1 g=84 m=169420 gstatus=2 +GCMarkAssistActive dt=1 g=84 +GCMarkAssistEnd dt=3 +HeapAlloc dt=20 heapalloc_value=190365120 +GCMarkAssistBegin dt=42 stack=3 +GoStop dt=861 reason_string=20 stack=9 +GoStart dt=142 g=126 g_seq=1 +GoBlock dt=2538 reason_string=13 stack=11 +GoUnblock dt=1653 g=30 g_seq=1 stack=0 +GoStart dt=7 g=30 g_seq=2 +GoLabel dt=2 label_string=4 +GoBlock dt=6064 reason_string=15 stack=5 +GoUnblock dt=1633 g=25 g_seq=11 stack=0 +GoStart dt=8 g=25 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=4927 reason_string=15 stack=5 +GoUnblock dt=3569 g=67 g_seq=11 stack=0 +GoStart dt=7 g=67 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=1289 reason_string=15 stack=5 +GoUnblock dt=73 g=67 g_seq=13 stack=0 +GoStart dt=4 g=67 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=46 reason_string=15 stack=5 +ProcStop dt=52 +EventBatch gen=3 m=169419 time=28114950898971 size=132 +ProcStatus dt=2 p=13 pstatus=1 +GoStatus dt=2 g=30 m=169419 gstatus=2 +GoBlock dt=7 reason_string=15 stack=5 +GoUnblock dt=2697 g=72 g_seq=1 stack=0 +GoStart dt=8 g=72 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=969 reason_string=15 stack=5 +GoStart dt=2978 g=95 g_seq=2 +GoStatus dt=32 g=88 m=18446744073709551615 gstatus=4 +GoUnblock dt=7 g=88 g_seq=1 stack=10 +GCMarkAssistBegin dt=18 stack=3 +GoStop dt=1258 reason_string=20 stack=9 +GoStart dt=17 g=88 g_seq=2 +GoStatus dt=27 g=113 m=18446744073709551615 gstatus=4 +GoUnblock dt=5 g=113 g_seq=1 stack=10 +GCMarkAssistBegin dt=12 stack=3 +GoStop dt=1797 reason_string=20 stack=9 +GoStart dt=18 g=88 g_seq=3 +GoStop dt=2883 reason_string=20 stack=9 +GoUnblock dt=14 g=70 g_seq=7 stack=0 +GoStart dt=5 g=70 g_seq=8 +GoLabel dt=3 label_string=2 +GoBlock dt=5294 reason_string=15 stack=5 +GoStart dt=14 g=123 g_seq=5 +GoBlock dt=18 reason_string=13 stack=11 +ProcStop dt=16 +EventBatch gen=3 m=169418 time=28114950895095 size=398 +ProcStatus dt=1 p=35 pstatus=2 +ProcStart dt=2 p=35 p_seq=1 +GoStart dt=38 g=87 g_seq=1 +HeapAlloc dt=103 heapalloc_value=190086592 +GCMarkAssistBegin dt=64 stack=3 +GCMarkAssistEnd dt=3228 +HeapAlloc dt=76 heapalloc_value=190995904 +GCMarkAssistBegin dt=149 stack=3 +GoBlock dt=3823 reason_string=13 stack=11 +GoStart dt=1406 g=82 g_seq=4 +GCMarkAssistEnd dt=12 +HeapAlloc dt=82 heapalloc_value=191618496 +GCMarkAssistBegin dt=75 stack=3 +GoStop dt=4342 reason_string=20 stack=9 +GoStart dt=17 g=82 g_seq=5 +GoStop dt=987 reason_string=20 stack=9 +GoStart dt=26 g=82 g_seq=6 +GoStop dt=3601 reason_string=20 stack=9 +GoUnblock dt=14 g=58 g_seq=17 stack=0 +GoStart dt=3 g=58 g_seq=18 +GoLabel dt=3 label_string=2 +GoBlock dt=1524 reason_string=15 stack=5 +GoUnblock dt=23 g=25 g_seq=15 stack=0 +GoStart dt=8 g=25 g_seq=16 +GoLabel dt=3 label_string=2 +GoBlock dt=7942 reason_string=15 stack=5 +ProcStop dt=2920 +ProcStart dt=69 p=31 p_seq=1 +GoUnblock dt=7 g=67 g_seq=25 stack=0 +GoStart dt=5 g=67 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=1990 reason_string=15 stack=5 +GoUnblock dt=110 g=67 g_seq=29 stack=0 +GoStart dt=6 g=67 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=39 reason_string=15 stack=5 +GoUnblock dt=64 g=52 g_seq=29 stack=0 +GoStart dt=1 g=52 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=40 reason_string=15 stack=5 +GoUnblock dt=72 g=29 g_seq=19 stack=0 +GoStart dt=7 g=29 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=65 reason_string=15 stack=5 +GoUnblock dt=1007 g=23 g_seq=43 stack=0 +GoStart dt=8 g=23 g_seq=44 +GoLabel dt=1 label_string=4 +GoBlock dt=1633 reason_string=15 stack=5 +GoUnblock dt=7 g=23 g_seq=45 stack=0 +GoStart dt=160 g=23 g_seq=46 +GoLabel dt=1 label_string=2 +GoBlock dt=16 reason_string=15 stack=5 +ProcStop dt=31 +ProcStart dt=8279 p=26 p_seq=2 +GoStart dt=216 g=103 g_seq=3 +GCMarkAssistBegin dt=19 stack=3 +GoBlock dt=41 reason_string=13 stack=11 +GoUnblock dt=11 g=25 g_seq=47 stack=0 +GoStart dt=3 g=25 g_seq=48 +GoLabel dt=1 label_string=2 +GoBlock dt=1274 reason_string=15 stack=5 +ProcStop dt=46 +ProcStart dt=1294 p=26 p_seq=3 +GoStart dt=164 g=127 g_seq=6 +GoStop dt=45 reason_string=20 stack=9 +GoStart dt=17 g=127 g_seq=7 +GCMarkAssistEnd dt=1921 +GoStop dt=49 reason_string=16 stack=17 +GoUnblock dt=17 g=22 g_seq=59 stack=0 +GoStart dt=8 g=22 g_seq=60 +GoLabel dt=1 label_string=2 +GoBlock dt=75 reason_string=15 stack=5 +GoStart dt=44 g=83 g_seq=7 +GCMarkAssistEnd dt=4 +GCMarkAssistBegin dt=50 stack=3 +GCMarkAssistEnd dt=27 +HeapAlloc dt=55 heapalloc_value=193551064 +GoStop dt=47 reason_string=16 stack=4 +GoStart dt=30 g=84 g_seq=7 +HeapAlloc dt=82 heapalloc_value=193772248 +HeapAlloc dt=291 heapalloc_value=194239192 +HeapAlloc dt=198 heapalloc_value=194493144 +HeapAlloc dt=7678 heapalloc_value=196524496 +GoStop dt=18 reason_string=16 stack=6 +ProcStop dt=218 +ProcStart dt=2082 p=26 p_seq=4 +ProcStop dt=68 +ProcStart dt=16818 p=14 p_seq=2 +GoStart dt=242 g=118 g_seq=7 +GCMarkAssistEnd dt=8 +GCSweepBegin dt=32 stack=28 +GCSweepEnd dt=11 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=3 heapalloc_value=114809728 +GCSweepBegin dt=279 stack=27 +EventBatch gen=3 m=169417 time=28114950894785 size=650 +ProcStatus dt=2 p=18 pstatus=1 +GoStatus dt=2 g=120 m=169417 gstatus=2 +GCMarkAssistActive dt=1 g=120 +GCMarkAssistEnd dt=2 +HeapAlloc dt=26 heapalloc_value=189767104 +GoStop dt=131 reason_string=16 stack=4 +GoStart dt=59 g=120 g_seq=1 +HeapAlloc dt=138 heapalloc_value=190045632 +GCMarkAssistBegin dt=31 stack=3 +GCMarkAssistEnd dt=3339 +HeapAlloc dt=63 heapalloc_value=190938560 +GCMarkAssistBegin dt=150 stack=3 +GoBlock dt=270 reason_string=13 stack=11 +GoUnblock dt=4058 g=57 g_seq=3 stack=0 +GoStart dt=7 g=57 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=902 reason_string=15 stack=5 +GoUnblock dt=4049 g=30 g_seq=3 stack=0 +GoStart dt=8 g=30 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=521 reason_string=15 stack=5 +GoUnblock dt=1581 g=57 g_seq=17 stack=0 +GoStart dt=11 g=57 g_seq=18 +GoLabel dt=3 label_string=4 +GoBlock dt=37 reason_string=15 stack=5 +GoUnblock dt=14 g=69 g_seq=11 stack=0 +GoStart dt=4 g=69 g_seq=12 +GoLabel dt=3 label_string=2 +GoBlock dt=543 reason_string=15 stack=5 +GoUnblock dt=10 g=69 g_seq=13 stack=0 +GoStart dt=6 g=69 g_seq=14 +GoLabel dt=1 label_string=2 +GoBlock dt=1813 reason_string=15 stack=5 +ProcStop dt=2875 +ProcStart dt=28 p=18 p_seq=1 +GoUnblock dt=1296 g=54 g_seq=23 stack=0 +GoStart dt=7 g=54 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=1516 reason_string=15 stack=5 +GoUnblock dt=7 g=54 g_seq=25 stack=0 +GoStart dt=5 g=54 g_seq=26 +GoLabel dt=1 label_string=2 +GoBlock dt=6851 reason_string=15 stack=5 +GoUnblock dt=71 g=72 g_seq=41 stack=0 +GoStart dt=5 g=72 g_seq=42 +GoLabel dt=1 label_string=4 +GoBlock dt=21 reason_string=15 stack=5 +GoUnblock dt=5 g=72 g_seq=43 stack=0 +GoStart dt=3 g=72 g_seq=44 +GoLabel dt=1 label_string=2 +GoBlock dt=62 reason_string=15 stack=5 +GoUnblock dt=19 g=72 g_seq=45 stack=0 +GoStart dt=4 g=72 g_seq=46 +GoLabel dt=1 label_string=2 +GoBlock dt=3727 reason_string=15 stack=5 +ProcStop dt=69 +ProcStart dt=21 p=18 p_seq=2 +GoUnblock dt=10 g=70 g_seq=15 stack=0 +GoStart dt=3 g=70 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=14 reason_string=15 stack=5 +GoUnblock dt=49 g=70 g_seq=17 stack=0 +GoStart dt=3 g=70 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=2314 reason_string=15 stack=5 +ProcStop dt=46 +ProcStart dt=1398 p=18 p_seq=3 +ProcStop dt=38 +ProcStart dt=4183 p=18 p_seq=4 +GoStart dt=183 g=96 g_seq=4 +GCMarkAssistEnd dt=9 +HeapAlloc dt=36 heapalloc_value=192305952 +GCMarkAssistBegin dt=28 stack=3 +GoBlock dt=1320 reason_string=13 stack=11 +GoUnblock dt=15 g=25 g_seq=45 stack=0 +GoStart dt=7 g=25 g_seq=46 +GoLabel dt=1 label_string=2 +GoBlock dt=600 reason_string=15 stack=5 +GoStart dt=13 g=89 g_seq=5 +GCMarkAssistBegin dt=11 stack=3 +GoBlock dt=112 reason_string=13 stack=11 +GoStart dt=14 g=121 g_seq=8 +GoStop dt=1488 reason_string=20 stack=9 +GoUnblock dt=16 g=25 g_seq=49 stack=0 +GoStart dt=7 g=25 g_seq=50 +GoLabel dt=2 label_string=2 +GoUnblock dt=1803 g=115 g_seq=6 stack=12 +GoUnblock dt=5 g=93 g_seq=6 stack=12 +GoUnblock dt=6 g=85 g_seq=5 stack=12 +GoUnblock dt=3 g=104 g_seq=1 stack=12 +GoUnblock dt=6 g=108 g_seq=1 stack=12 +GoUnblock dt=4 g=120 g_seq=4 stack=12 +GoUnblock dt=4 g=126 g_seq=5 stack=12 +GoUnblock dt=7 g=114 g_seq=3 stack=12 +GoUnblock dt=5 g=86 g_seq=4 stack=12 +GoUnblock dt=4 g=110 g_seq=3 stack=12 +GoBlock dt=14 reason_string=15 stack=5 +GoUnblock dt=7 g=25 g_seq=51 stack=0 +GoStart dt=1 g=25 g_seq=52 +GoLabel dt=1 label_string=2 +GoBlock dt=12 reason_string=15 stack=5 +GoStart dt=8 g=115 g_seq=7 +GCMarkAssistEnd dt=6 +HeapAlloc dt=32 heapalloc_value=192838360 +GCMarkAssistBegin dt=55 stack=3 +GoStop dt=1501 reason_string=20 stack=9 +GoUnblock dt=18 g=51 g_seq=51 stack=0 +GoStart dt=6 g=51 g_seq=52 +GoLabel dt=1 label_string=2 +GoUnblock dt=1117 g=105 g_seq=13 stack=12 +GoBlock dt=8 reason_string=15 stack=5 +GoStart dt=8 g=105 g_seq=14 +GCMarkAssistEnd dt=6 +GCMarkAssistBegin dt=27 stack=3 +GoBlock dt=13 reason_string=13 stack=11 +GoStart dt=7 g=86 g_seq=5 +GCMarkAssistEnd dt=6 +HeapAlloc dt=12 heapalloc_value=195148504 +GCMarkAssistBegin dt=65 stack=3 +HeapAlloc dt=22 heapalloc_value=195214040 +GoBlock dt=17 reason_string=10 stack=18 +GoStart dt=2881 g=124 g_seq=5 +HeapAlloc dt=35 heapalloc_value=195517144 +HeapAlloc dt=15 heapalloc_value=195566296 +HeapAlloc dt=61 heapalloc_value=195574224 +HeapAlloc dt=16 heapalloc_value=195631568 +GCMarkAssistBegin dt=23 stack=3 +GoBlock dt=34 reason_string=10 stack=18 +GoStart dt=14 g=90 g_seq=7 +GCMarkAssistEnd dt=5 +HeapAlloc dt=4 heapalloc_value=195697104 +GCMarkAssistBegin dt=58 stack=3 +GoBlock dt=15 reason_string=10 stack=18 +GoStart dt=10 g=96 g_seq=6 +GCMarkAssistEnd dt=3 +GCMarkAssistBegin dt=37 stack=3 +GoBlock dt=16 reason_string=13 stack=11 +GoStart dt=14 g=92 g_seq=5 +GCMarkAssistBegin dt=75 stack=3 +GoBlock dt=25 reason_string=10 stack=18 +GoStart dt=9 g=119 g_seq=6 +GCMarkAssistEnd dt=5 +HeapAlloc dt=20 heapalloc_value=195926480 +GCMarkAssistBegin dt=19 stack=3 +GoBlock dt=14 reason_string=10 stack=18 +GoStart dt=9 g=85 g_seq=6 +GCMarkAssistEnd dt=3 +HeapAlloc dt=38 heapalloc_value=195983824 +GoStop dt=5763 reason_string=16 stack=6 +GoStart dt=15 g=85 g_seq=7 +GCMarkAssistBegin dt=6 stack=3 +GoBlock dt=21 reason_string=10 stack=18 +ProcStop dt=46 +ProcStart dt=17429 p=15 p_seq=2 +GoStart dt=180 g=3 g_seq=2 +EventBatch gen=3 m=169416 time=28114950894874 size=516 +ProcStatus dt=2 p=26 pstatus=1 +GoStatus dt=1 g=98 m=169416 gstatus=2 +GCMarkAssistActive dt=1 g=98 +GCMarkAssistEnd dt=2 +HeapAlloc dt=136 heapalloc_value=190004672 +GoStop dt=29 reason_string=16 stack=4 +GoStart dt=29 g=86 g_seq=1 +GCMarkAssistBegin dt=75 stack=3 +GCMarkAssistEnd dt=8456 +HeapAlloc dt=48 heapalloc_value=191569344 +GoStop dt=32 reason_string=16 stack=4 +GoStart dt=19 g=86 g_seq=2 +GCMarkAssistBegin dt=73 stack=3 +GoStop dt=324 reason_string=20 stack=9 +GoStart dt=11 g=116 g_seq=3 +GoStop dt=270 reason_string=20 stack=9 +GoUnblock dt=14 g=51 g_seq=7 stack=0 +GoStart dt=4 g=51 g_seq=8 +GoLabel dt=3 label_string=2 +GoBlock dt=4139 reason_string=15 stack=5 +GoUnblock dt=9 g=51 g_seq=9 stack=0 +GoStart dt=6 g=51 g_seq=10 +GoLabel dt=1 label_string=2 +GoBlock dt=564 reason_string=15 stack=5 +GoUnblock dt=12 g=29 g_seq=7 stack=0 +GoStart dt=4 g=29 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=13475 reason_string=15 stack=5 +ProcStop dt=61 +ProcStart dt=17 p=26 p_seq=1 +GoUnblock dt=6 g=53 g_seq=31 stack=0 +GoStart dt=3 g=53 g_seq=32 +GoLabel dt=1 label_string=4 +GoBlock dt=18 reason_string=15 stack=5 +GoUnblock dt=6 g=53 g_seq=33 stack=0 +GoStart dt=4 g=53 g_seq=34 +GoLabel dt=1 label_string=2 +GoBlock dt=2291 reason_string=15 stack=5 +GoUnblock dt=8 g=53 g_seq=35 stack=0 +GoStart dt=4 g=53 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=32 reason_string=15 stack=5 +GoUnblock dt=68 g=29 g_seq=9 stack=0 +GoStart dt=4 g=29 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=796 reason_string=15 stack=5 +GoUnblock dt=60 g=29 g_seq=11 stack=0 +GoStart dt=4 g=29 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=643 reason_string=15 stack=5 +GoUnblock dt=61 g=53 g_seq=43 stack=0 +GoStart dt=4 g=53 g_seq=44 +GoLabel dt=1 label_string=4 +GoBlock dt=3485 reason_string=15 stack=5 +GoUnblock dt=10 g=53 g_seq=45 stack=0 +GoStart dt=5 g=53 g_seq=46 +GoLabel dt=1 label_string=2 +GoBlock dt=14 reason_string=15 stack=5 +ProcStop dt=38 +ProcStart dt=9443 p=0 p_seq=3 +GoStart dt=226 g=115 g_seq=5 +GoBlock dt=168 reason_string=13 stack=11 +GoUnblock dt=11 g=30 g_seq=37 stack=0 +GoStart dt=6 g=30 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=67 reason_string=15 stack=5 +ProcStop dt=46 +ProcStart dt=1842 p=25 p_seq=6 +GoUnblock dt=12 g=29 g_seq=37 stack=0 +GoStart dt=5 g=29 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=2260 reason_string=15 stack=5 +GoUnblock dt=16 g=29 g_seq=39 stack=0 +GoStart dt=4 g=29 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=19 reason_string=15 stack=5 +GoStart dt=34 g=99 g_seq=4 +GCMarkAssistEnd dt=7 +HeapAlloc dt=55 heapalloc_value=193501912 +GoStop dt=29 reason_string=16 stack=4 +GoUnblock dt=18 g=29 g_seq=41 stack=0 +GoStart dt=7 g=29 g_seq=42 +GoLabel dt=1 label_string=2 +GoBlock dt=10 reason_string=15 stack=5 +GoUnblock dt=14 g=29 g_seq=43 stack=0 +GoStart dt=4 g=29 g_seq=44 +GoLabel dt=1 label_string=2 +GoBlock dt=40 reason_string=15 stack=5 +GoStart dt=16 g=111 g_seq=6 +GoBlock dt=37 reason_string=13 stack=11 +GoStart dt=13 g=125 g_seq=6 +GCMarkAssistBegin dt=13 stack=3 +GoBlock dt=34 reason_string=13 stack=11 +GoStart dt=23 g=115 g_seq=8 +GoBlock dt=61 reason_string=13 stack=11 +GoStart dt=27 g=120 g_seq=5 +GCMarkAssistEnd dt=12 +HeapAlloc dt=82 heapalloc_value=194067160 +GoStop dt=22 reason_string=16 stack=4 +GoStart dt=10 g=93 g_seq=7 +GCMarkAssistEnd dt=4 +HeapAlloc dt=663 heapalloc_value=194992856 +GCMarkAssistBegin dt=23 stack=3 +GoBlock dt=12 reason_string=13 stack=11 +GoStart dt=11 g=99 g_seq=7 +GCMarkAssistEnd dt=5 +HeapAlloc dt=4741 heapalloc_value=196180432 +GoStop dt=10 reason_string=16 stack=6 +GoStart dt=19 g=99 g_seq=8 +GCMarkAssistBegin dt=8 stack=3 +GoBlock dt=18 reason_string=10 stack=18 +GoStart dt=9 g=100 g_seq=4 +GCMarkAssistEnd dt=5 +HeapAlloc dt=101 heapalloc_value=196295120 +GoStop dt=6074 reason_string=16 stack=6 +GoStart dt=49 g=100 g_seq=5 +GCMarkAssistBegin dt=10 stack=3 +GoBlock dt=32 reason_string=13 stack=11 +ProcStop dt=67 +ProcStart dt=12947 p=10 p_seq=3 +GoStart dt=200 g=86 g_seq=7 +GoUnblock dt=38 g=124 g_seq=6 stack=30 +GCMarkAssistEnd dt=5 +HeapAlloc dt=90 heapalloc_value=113809792 +HeapAlloc dt=112 heapalloc_value=114160256 +GCSweepBegin dt=694 stack=31 +EventBatch gen=3 m=169415 time=28114950903030 size=633 +ProcStatus dt=1 p=28 pstatus=1 +GoStatus dt=3 g=91 m=169415 gstatus=2 +GCMarkAssistActive dt=1 g=91 +GCMarkAssistEnd dt=2 +HeapAlloc dt=29 heapalloc_value=191479232 +GCMarkAssistBegin dt=84 stack=3 +GoBlock dt=82 reason_string=13 stack=11 +GoStart dt=4920 g=113 g_seq=2 +GoStatus dt=31 g=123 m=18446744073709551615 gstatus=4 +GoUnblock dt=10 g=123 g_seq=1 stack=10 +GCMarkAssistBegin dt=14 stack=3 +GoStop dt=1855 reason_string=20 stack=9 +GoStart dt=15 g=113 g_seq=3 +GoStop dt=352 reason_string=20 stack=9 +GoStart dt=13 g=113 g_seq=4 +GoBlock dt=261 reason_string=13 stack=11 +GoUnblock dt=3404 g=52 g_seq=17 stack=0 +GoStart dt=7 g=52 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=1025 reason_string=15 stack=5 +GoUnblock dt=4703 g=67 g_seq=17 stack=0 +GoStart dt=8 g=67 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=1418 reason_string=15 stack=5 +GoUnblock dt=72 g=23 g_seq=29 stack=0 +GoStart dt=4 g=23 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=307 reason_string=15 stack=5 +GoUnblock dt=85 g=72 g_seq=33 stack=0 +GoStart dt=5 g=72 g_seq=34 +GoLabel dt=3 label_string=4 +GoBlock dt=30 reason_string=15 stack=5 +GoStatus dt=168 g=68 m=18446744073709551615 gstatus=4 +GoUnblock dt=2 g=68 g_seq=1 stack=0 +GoStart dt=64 g=68 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=55 reason_string=15 stack=5 +GoUnblock dt=10 g=68 g_seq=3 stack=0 +GoStart dt=3 g=68 g_seq=4 +GoLabel dt=2 label_string=2 +GoBlock dt=327 reason_string=15 stack=5 +ProcStop dt=80 +ProcStart dt=25 p=28 p_seq=1 +GoUnblock dt=7 g=30 g_seq=17 stack=0 +GoStart dt=4 g=30 g_seq=18 +GoLabel dt=3 label_string=4 +GoBlock dt=2630 reason_string=15 stack=5 +GoUnblock dt=28 g=72 g_seq=39 stack=0 +GoStart dt=12 g=72 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=21 reason_string=15 stack=5 +GoUnblock dt=77 g=30 g_seq=19 stack=0 +GoStart dt=10 g=30 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=3781 reason_string=15 stack=5 +GoUnblock dt=15 g=30 g_seq=21 stack=0 +GoStart dt=5 g=30 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=2537 reason_string=15 stack=5 +GoUnblock dt=55 g=30 g_seq=23 stack=0 +GoStart dt=5 g=30 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=478 reason_string=15 stack=5 +GoUnblock dt=8 g=30 g_seq=25 stack=0 +GoStart dt=4 g=30 g_seq=26 +GoLabel dt=1 label_string=2 +GoBlock dt=1039 reason_string=15 stack=5 +GoStart dt=26 g=14 g_seq=37 +GoBlock dt=1631 reason_string=15 stack=5 +GoUnblock dt=22 g=52 g_seq=43 stack=0 +GoStart dt=8 g=52 g_seq=44 +GoLabel dt=3 label_string=2 +GoBlock dt=21 reason_string=15 stack=5 +GoUnblock dt=9 g=52 g_seq=45 stack=0 +GoStart dt=3 g=52 g_seq=46 +GoLabel dt=1 label_string=2 +GoBlock dt=17 reason_string=15 stack=5 +GoUnblock dt=6 g=52 g_seq=47 stack=0 +GoStart dt=3 g=52 g_seq=48 +GoLabel dt=1 label_string=2 +GoUnblock dt=217 g=112 g_seq=3 stack=12 +GoBlock dt=298 reason_string=15 stack=5 +GoUnblock dt=8 g=52 g_seq=49 stack=0 +GoStart dt=3 g=52 g_seq=50 +GoLabel dt=1 label_string=2 +GoBlock dt=1919 reason_string=15 stack=5 +GoStart dt=16 g=121 g_seq=6 +GCMarkAssistEnd dt=6 +HeapAlloc dt=1354 heapalloc_value=192363224 +GoStop dt=25 reason_string=16 stack=4 +GoStart dt=16 g=121 g_seq=7 +GCMarkAssistBegin dt=74 stack=3 +GoStop dt=496 reason_string=20 stack=9 +GoUnblock dt=11 g=52 g_seq=55 stack=0 +GoStart dt=4 g=52 g_seq=56 +GoLabel dt=1 label_string=2 +GoUnblock dt=1666 g=94 g_seq=5 stack=12 +GoBlock dt=18 reason_string=15 stack=5 +GoUnblock dt=18 g=30 g_seq=41 stack=0 +GoStart dt=4 g=30 g_seq=42 +GoLabel dt=1 label_string=2 +GoUnblock dt=1362 g=84 g_seq=5 stack=12 +GoUnblock dt=6 g=125 g_seq=4 stack=12 +GoUnblock dt=5 g=118 g_seq=3 stack=12 +GoBlock dt=9 reason_string=15 stack=5 +GoUnblock dt=10 g=30 g_seq=43 stack=0 +GoStart dt=3 g=30 g_seq=44 +GoLabel dt=1 label_string=2 +GoBlock dt=9 reason_string=15 stack=5 +GoStart dt=6 g=84 g_seq=6 +GCMarkAssistEnd dt=5 +HeapAlloc dt=24 heapalloc_value=192748248 +GCMarkAssistBegin dt=83 stack=3 +GCMarkAssistEnd dt=1516 +HeapAlloc dt=28 heapalloc_value=193231576 +GoStop dt=27 reason_string=16 stack=6 +GoUnblock dt=14 g=22 g_seq=57 stack=0 +GoStart dt=3 g=22 g_seq=58 +GoLabel dt=1 label_string=2 +GoUnblock dt=16 g=81 g_seq=8 stack=12 +GoBlock dt=10 reason_string=15 stack=5 +GoStart dt=11 g=125 g_seq=5 +GCMarkAssistEnd dt=5 +HeapAlloc dt=16 heapalloc_value=193354456 +GoStop dt=95 reason_string=16 stack=6 +GoUnblock dt=34 g=22 g_seq=61 stack=0 +GoStart dt=1 g=22 g_seq=62 +GoLabel dt=1 label_string=2 +GoUnblock dt=1090 g=99 g_seq=6 stack=12 +GoBlock dt=10 reason_string=15 stack=5 +GoStart dt=8 g=81 g_seq=9 +GCMarkAssistEnd dt=5 +HeapAlloc dt=3528 heapalloc_value=195729872 +GoStop dt=10 reason_string=16 stack=6 +GoStart dt=17 g=81 g_seq=10 +GCMarkAssistBegin dt=9 stack=3 +GoBlock dt=34 reason_string=10 stack=18 +GoStart dt=20 g=121 g_seq=11 +GCMarkAssistEnd dt=4 +HeapAlloc dt=44 heapalloc_value=195852752 +GoStop dt=7425 reason_string=16 stack=6 +ProcStop dt=134 +ProcStart dt=14156 p=12 p_seq=2 +GoStart dt=200 g=84 g_seq=10 +GCMarkAssistEnd dt=6 +GCSweepBegin dt=35 stack=27 +EventBatch gen=3 m=169414 time=28114950903409 size=415 +ProcStatus dt=1 p=19 pstatus=1 +GoStatus dt=1 g=54 m=169414 gstatus=2 +GoBlock dt=7 reason_string=15 stack=5 +GoUnblock dt=2586 g=25 g_seq=5 stack=0 +GoStart dt=8 g=25 g_seq=6 +GoLabel dt=1 label_string=4 +GoBlock dt=2605 reason_string=15 stack=5 +GoUnblock dt=1216 g=71 g_seq=3 stack=0 +GoStart dt=7 g=71 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=672 reason_string=15 stack=5 +GoUnblock dt=7231 g=23 g_seq=15 stack=0 +GoStart dt=8 g=23 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=1212 reason_string=15 stack=5 +GoUnblock dt=11 g=23 g_seq=17 stack=0 +GoStart dt=7 g=23 g_seq=18 +GoLabel dt=3 label_string=2 +GoBlock dt=82 reason_string=15 stack=5 +GoUnblock dt=9 g=23 g_seq=19 stack=0 +GoStart dt=6 g=23 g_seq=20 +GoLabel dt=1 label_string=2 +GoBlock dt=162 reason_string=15 stack=5 +ProcStop dt=99 +ProcStart dt=3257 p=19 p_seq=1 +GoUnblock dt=13 g=68 g_seq=5 stack=0 +GoStart dt=10 g=68 g_seq=6 +GoLabel dt=1 label_string=2 +GoBlock dt=43 reason_string=15 stack=5 +GoUnblock dt=12 g=68 g_seq=7 stack=0 +GoStart dt=2 g=68 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=133 reason_string=15 stack=5 +GoUnblock dt=23 g=58 g_seq=23 stack=0 +GoStart dt=6 g=58 g_seq=24 +GoLabel dt=1 label_string=2 +GoBlock dt=2822 reason_string=15 stack=5 +GoUnblock dt=11 g=69 g_seq=21 stack=0 +GoStart dt=7 g=69 g_seq=22 +GoLabel dt=2 label_string=2 +GoBlock dt=25 reason_string=15 stack=5 +GoUnblock dt=2937 g=58 g_seq=31 stack=0 +GoStart dt=6 g=58 g_seq=32 +GoLabel dt=1 label_string=4 +GoBlock dt=20 reason_string=15 stack=5 +ProcStop dt=60 +ProcStart dt=31 p=19 p_seq=2 +GoUnblock dt=9 g=56 g_seq=7 stack=0 +GoStart dt=6 g=56 g_seq=8 +GoLabel dt=3 label_string=4 +GoBlock dt=949 reason_string=15 stack=5 +ProcStop dt=41 +ProcStart dt=433 p=19 p_seq=3 +ProcStop dt=43 +ProcStart dt=9942 p=11 p_seq=4 +ProcStop dt=50 +ProcStart dt=2351 p=22 p_seq=6 +GoUnblock dt=15 g=30 g_seq=45 stack=0 +GoStart dt=205 g=30 g_seq=46 +GoLabel dt=1 label_string=2 +GoUnblock dt=145 g=113 g_seq=7 stack=12 +GoBlock dt=21 reason_string=15 stack=5 +GoStart dt=10 g=113 g_seq=8 +GCMarkAssistEnd dt=8 +HeapAlloc dt=48 heapalloc_value=192895704 +GCMarkAssistBegin dt=118 stack=3 +GCMarkAssistEnd dt=272 +HeapAlloc dt=20 heapalloc_value=192936664 +HeapAlloc dt=89 heapalloc_value=192953048 +HeapAlloc dt=41 heapalloc_value=192994008 +HeapAlloc dt=92 heapalloc_value=193059544 +HeapAlloc dt=102 heapalloc_value=193108696 +HeapAlloc dt=94 heapalloc_value=193133272 +HeapAlloc dt=42 heapalloc_value=193141464 +HeapAlloc dt=31 heapalloc_value=193207000 +GCMarkAssistBegin dt=142 stack=3 +GoBlock dt=114 reason_string=13 stack=11 +GoStart dt=179 g=109 g_seq=5 +GCMarkAssistEnd dt=8 +GCMarkAssistBegin dt=54 stack=3 +GCMarkAssistEnd dt=720 +HeapAlloc dt=23 heapalloc_value=194427608 +HeapAlloc dt=456 heapalloc_value=195001048 +GCMarkAssistBegin dt=18 stack=3 +GoBlock dt=22 reason_string=13 stack=11 +GoStart dt=23 g=113 g_seq=10 +GCMarkAssistEnd dt=3 +HeapAlloc dt=54 heapalloc_value=195099352 +GoStop dt=6390 reason_string=16 stack=6 +GoStart dt=23 g=113 g_seq=11 +GCMarkAssistBegin dt=6 stack=3 +GoBlock dt=21 reason_string=10 stack=18 +GoStart dt=33 g=101 g_seq=6 +GCMarkAssistEnd dt=6 +HeapAlloc dt=29 heapalloc_value=196409808 +GCMarkAssistBegin dt=22 stack=3 +GoBlock dt=52 reason_string=10 stack=18 +ProcStop dt=102 +EventBatch gen=3 m=169413 time=28114950897164 size=752 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=6 g=67 m=169413 gstatus=2 +GoBlock dt=11 reason_string=15 stack=5 +GoUnblock dt=18 g=25 g_seq=1 stack=0 +GoStart dt=7 g=25 g_seq=2 +GoLabel dt=1 label_string=2 +GoBlock dt=1315 reason_string=15 stack=5 +GoUnblock dt=11 g=25 g_seq=3 stack=0 +GoStart dt=6 g=25 g_seq=4 +GoLabel dt=1 label_string=2 +GoUnblock dt=4173 g=106 g_seq=1 stack=12 +GoBlock dt=1258 reason_string=15 stack=5 +GoUnblock dt=4804 g=30 g_seq=5 stack=0 +GoStart dt=7 g=30 g_seq=6 +GoLabel dt=1 label_string=4 +GoBlock dt=541 reason_string=15 stack=5 +GoUnblock dt=30 g=30 g_seq=7 stack=0 +GoStart dt=6 g=30 g_seq=8 +GoLabel dt=3 label_string=2 +GoBlock dt=3873 reason_string=15 stack=5 +GoUnblock dt=10 g=30 g_seq=9 stack=0 +GoStart dt=5 g=30 g_seq=10 +GoLabel dt=3 label_string=2 +GoBlock dt=3107 reason_string=15 stack=5 +GoUnblock dt=3672 g=14 g_seq=15 stack=0 +GoStart dt=6 g=14 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=442 reason_string=15 stack=5 +GoStart dt=32 g=83 g_seq=4 +GCMarkAssistEnd dt=7 +HeapAlloc dt=49 heapalloc_value=191962560 +GCMarkAssistBegin dt=108 stack=3 +GoStop dt=885 reason_string=20 stack=9 +GoStart dt=14 g=83 g_seq=5 +GoBlock dt=21 reason_string=13 stack=11 +ProcStop dt=93 +ProcStart dt=38 p=0 p_seq=1 +GoUnblock dt=7 g=53 g_seq=17 stack=0 +GoStart dt=2 g=53 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=31 reason_string=15 stack=5 +ProcStop dt=89 +ProcStart dt=45 p=11 p_seq=3 +GoUnblock dt=6 g=23 g_seq=35 stack=0 +GoStart dt=14 g=23 g_seq=36 +GoLabel dt=3 label_string=4 +GoBlock dt=2881 reason_string=15 stack=5 +GoUnblock dt=72 g=25 g_seq=17 stack=0 +GoStart dt=6 g=25 g_seq=18 +GoLabel dt=1 label_string=4 +GoBlock dt=19 reason_string=15 stack=5 +GoUnblock dt=58 g=25 g_seq=19 stack=0 +GoStart dt=3 g=25 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=13 reason_string=15 stack=5 +GoStart dt=16 g=94 g_seq=4 +GoBlock dt=356 reason_string=13 stack=11 +GoUnblock dt=80 g=52 g_seq=27 stack=0 +GoStart dt=9 g=52 g_seq=28 +GoLabel dt=1 label_string=4 +GoBlock dt=2325 reason_string=15 stack=5 +GoUnblock dt=57 g=67 g_seq=31 stack=0 +GoStart dt=4 g=67 g_seq=32 +GoLabel dt=1 label_string=4 +GoBlock dt=2043 reason_string=15 stack=5 +GoUnblock dt=9 g=67 g_seq=33 stack=0 +GoStart dt=171 g=67 g_seq=34 +GoLabel dt=5 label_string=2 +GoBlock dt=21 reason_string=15 stack=5 +ProcStop dt=60 +ProcStart dt=1735 p=25 p_seq=4 +GoUnblock dt=61 g=22 g_seq=39 stack=0 +GoStart dt=178 g=22 g_seq=40 +GoLabel dt=1 label_string=4 +GoBlock dt=66 reason_string=15 stack=5 +GoUnblock dt=8 g=22 g_seq=41 stack=0 +GoStart dt=4 g=22 g_seq=42 +GoLabel dt=1 label_string=2 +GoBlock dt=975 reason_string=15 stack=5 +ProcStop dt=1192 +ProcStart dt=347 p=25 p_seq=5 +GoUnblock dt=11 g=131 g_seq=6 stack=0 +GoStart dt=145 g=131 g_seq=7 +GoBlock dt=21 reason_string=15 stack=2 +GoUnblock dt=30 g=14 g_seq=38 stack=0 +GoStart dt=4 g=14 g_seq=39 +GoLabel dt=1 label_string=2 +GoBlock dt=65 reason_string=15 stack=5 +GoStart dt=26 g=130 g_seq=1 +ProcStatus dt=380 p=38 pstatus=2 +ProcStatus dt=4 p=39 pstatus=2 +ProcStatus dt=4 p=40 pstatus=2 +ProcStatus dt=3 p=41 pstatus=2 +ProcStatus dt=5 p=42 pstatus=2 +ProcStatus dt=5 p=43 pstatus=2 +ProcStatus dt=2 p=44 pstatus=2 +ProcStatus dt=3 p=45 pstatus=2 +ProcStatus dt=4 p=46 pstatus=2 +GoStop dt=1488 reason_string=16 stack=15 +GoUnblock dt=17 g=51 g_seq=45 stack=0 +GoStart dt=3 g=51 g_seq=46 +GoLabel dt=3 label_string=2 +GoBlock dt=1337 reason_string=15 stack=5 +GoStart dt=13 g=81 g_seq=7 +GCMarkAssistEnd dt=6 +GCMarkAssistBegin dt=31 stack=3 +GoBlock dt=20 reason_string=13 stack=11 +GoStart dt=5 g=130 g_seq=2 +HeapAlloc dt=98 heapalloc_value=192314072 +GoBlock dt=348 reason_string=12 stack=16 +GoStart dt=31 g=103 g_seq=2 +GCMarkAssistEnd dt=7 +HeapAlloc dt=53 heapalloc_value=192428760 +GoStop dt=173 reason_string=16 stack=6 +GoUnblock dt=18 g=71 g_seq=29 stack=0 +GoStart dt=4 g=71 g_seq=30 +GoLabel dt=3 label_string=2 +GoBlock dt=1289 reason_string=15 stack=5 +GoStart dt=17 g=126 g_seq=4 +GCMarkAssistBegin dt=125 stack=3 +GoBlock dt=23 reason_string=13 stack=11 +ProcStop dt=76 +ProcStart dt=2523 p=0 p_seq=4 +GoUnblock dt=16 g=30 g_seq=47 stack=0 +GoStart dt=196 g=30 g_seq=48 +GoLabel dt=2 label_string=2 +GoUnblock dt=1834 g=125 g_seq=7 stack=12 +GoBlock dt=17 reason_string=15 stack=5 +GoStart dt=14 g=125 g_seq=8 +GCMarkAssistEnd dt=5 +HeapAlloc dt=69 heapalloc_value=194566872 +GoStop dt=2253 reason_string=16 stack=6 +GoStart dt=2080 g=125 g_seq=9 +GCMarkAssistBegin dt=14 stack=3 +GoBlock dt=41 reason_string=10 stack=18 +GoStart dt=13 g=106 g_seq=8 +GCMarkAssistEnd dt=6 +HeapAlloc dt=53 heapalloc_value=196106704 +GoStop dt=6900 reason_string=16 stack=6 +GoStart dt=57 g=121 g_seq=12 +GCMarkAssistBegin dt=16 stack=3 +GoBlock dt=47 reason_string=10 stack=18 +ProcStop dt=83 +ProcStart dt=11930 p=7 p_seq=7 +GoStart dt=191 g=96 g_seq=8 +GCMarkAssistEnd dt=10 +HeapAlloc dt=59 heapalloc_value=109727392 +HeapAlloc dt=159 heapalloc_value=110336128 +HeapAlloc dt=109 heapalloc_value=110662528 +GCSweepBegin dt=144 stack=28 +GCSweepEnd dt=18 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=3 heapalloc_value=111288448 +GCSweepBegin dt=49 stack=28 +GCSweepEnd dt=14 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=5 heapalloc_value=111591296 +HeapAlloc dt=65 heapalloc_value=111888256 +HeapAlloc dt=228 heapalloc_value=112797056 +HeapAlloc dt=134 heapalloc_value=113322880 +HeapAlloc dt=83 heapalloc_value=113549696 +GCSweepBegin dt=35 stack=28 +GCSweepEnd dt=16 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=113842560 +HeapAlloc dt=75 heapalloc_value=114080128 +HeapAlloc dt=64 heapalloc_value=114307712 +HeapAlloc dt=134 heapalloc_value=114580608 +HeapAlloc dt=77 heapalloc_value=114670464 +GCSweepBegin dt=33 stack=28 +GCSweepEnd dt=6 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=3 heapalloc_value=114727808 +GCSweepBegin dt=90 stack=27 +EventBatch gen=3 m=169412 time=28114950898429 size=583 +ProcStatus dt=1 p=36 pstatus=2 +ProcStart dt=2 p=36 p_seq=1 +GoStart dt=401 g=83 g_seq=2 +GoBlock dt=1477 reason_string=13 stack=11 +GoStart dt=1208 g=81 g_seq=2 +GCMarkAssistEnd dt=9 +HeapAlloc dt=57 heapalloc_value=191348160 +GoStop dt=42 reason_string=16 stack=4 +GoStart dt=25 g=81 g_seq=3 +GCMarkAssistBegin dt=394 stack=3 +GoBlock dt=1177 reason_string=13 stack=11 +GoStart dt=28 g=106 g_seq=2 +GCMarkAssistEnd dt=10 +HeapAlloc dt=52 heapalloc_value=191503808 +GCMarkAssistBegin dt=52 stack=3 +GoStop dt=60 reason_string=20 stack=9 +GoUnblock dt=73 g=58 g_seq=3 stack=0 +GoStart dt=6 g=58 g_seq=4 +GoLabel dt=3 label_string=4 +GoBlock dt=2860 reason_string=15 stack=5 +GoUnblock dt=3777 g=24 g_seq=9 stack=0 +GoStart dt=6 g=24 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=41 reason_string=15 stack=5 +GoUnblock dt=1167 g=71 g_seq=9 stack=0 +GoStart dt=7 g=71 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=1396 reason_string=15 stack=5 +GoUnblock dt=1371 g=57 g_seq=23 stack=0 +GoStart dt=7 g=57 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=584 reason_string=15 stack=5 +GoUnblock dt=4657 g=23 g_seq=23 stack=0 +GoStart dt=7 g=23 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=40 reason_string=15 stack=5 +ProcStop dt=82 +ProcStart dt=1505 p=36 p_seq=2 +ProcStop dt=74 +ProcStart dt=19 p=36 p_seq=3 +GoUnblock dt=7 g=23 g_seq=27 stack=0 +GoStart dt=7 g=23 g_seq=28 +GoLabel dt=1 label_string=4 +GoBlock dt=122 reason_string=15 stack=5 +GoUnblock dt=58 g=52 g_seq=25 stack=0 +GoStart dt=6 g=52 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=4034 reason_string=15 stack=5 +GoUnblock dt=75 g=14 g_seq=19 stack=0 +GoStart dt=6 g=14 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=2059 reason_string=15 stack=5 +GoUnblock dt=63 g=14 g_seq=21 stack=0 +GoStart dt=4 g=14 g_seq=22 +GoLabel dt=1 label_string=4 +GoBlock dt=56 reason_string=15 stack=5 +ProcStop dt=49 +ProcStart dt=20 p=36 p_seq=4 +GoUnblock dt=6 g=67 g_seq=27 stack=0 +GoStart dt=2 g=67 g_seq=28 +GoLabel dt=1 label_string=4 +GoBlock dt=13 reason_string=15 stack=5 +ProcStop dt=1721 +ProcStart dt=20316 p=36 p_seq=5 +GoStart dt=197 g=94 g_seq=11 +GCMarkAssistEnd dt=7 +HeapAlloc dt=6672 heapalloc_value=196598224 +GoStop dt=15 reason_string=16 stack=6 +GoStart dt=54 g=106 g_seq=9 +GCMarkAssistBegin dt=16 stack=3 +GoBlock dt=32 reason_string=10 stack=18 +GoStart dt=41 g=103 g_seq=6 +GCMarkAssistBegin dt=15 stack=3 +GoBlock dt=84 reason_string=10 stack=18 +ProcStop dt=43 +ProcStart dt=10888 p=5 p_seq=1 +GoStart dt=189 g=120 g_seq=8 +GCMarkAssistEnd dt=7 +HeapAlloc dt=54 heapalloc_value=106433440 +HeapAlloc dt=94 heapalloc_value=106861728 +GCSweepBegin dt=92 stack=28 +GCSweepEnd dt=13 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=4 heapalloc_value=107301920 +HeapAlloc dt=65 heapalloc_value=107394848 +GCSweepBegin dt=32 stack=28 +GCSweepEnd dt=11 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=2 heapalloc_value=107616032 +HeapAlloc dt=60 heapalloc_value=107763488 +HeapAlloc dt=78 heapalloc_value=107953440 +HeapAlloc dt=65 heapalloc_value=108333088 +GCSweepBegin dt=38 stack=28 +GCSweepEnd dt=5 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=1 heapalloc_value=108423200 +GCSweepBegin dt=80 stack=28 +GCSweepEnd dt=9 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=108682656 +GCSweepBegin dt=61 stack=28 +GCSweepEnd dt=10 swept_value=8192 reclaimed_value=8192 +HeapAlloc dt=4 heapalloc_value=108816544 +HeapAlloc dt=32 heapalloc_value=108994080 +HeapAlloc dt=50 heapalloc_value=109290272 +HeapAlloc dt=112 heapalloc_value=109566240 +HeapAlloc dt=104 heapalloc_value=109973280 +GCSweepBegin dt=66 stack=29 +GCSweepEnd dt=17 swept_value=8192 reclaimed_value=0 +HeapAlloc dt=3 heapalloc_value=110183040 +HeapAlloc dt=86 heapalloc_value=110506880 +HeapAlloc dt=149 heapalloc_value=111151232 +HeapAlloc dt=24 heapalloc_value=111272064 +HeapAlloc dt=53 heapalloc_value=111368064 +HeapAlloc dt=68 heapalloc_value=111632256 +HeapAlloc dt=103 heapalloc_value=112078720 +GCSweepBegin dt=120 stack=28 +GCSweepEnd dt=7 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=3 heapalloc_value=112585472 +HeapAlloc dt=34 heapalloc_value=112616832 +HeapAlloc dt=39 heapalloc_value=112882304 +HeapAlloc dt=141 heapalloc_value=113391232 +HeapAlloc dt=80 heapalloc_value=113664384 +HeapAlloc dt=152 heapalloc_value=114242176 +HeapAlloc dt=104 heapalloc_value=114415616 +HeapAlloc dt=38 heapalloc_value=114527360 +HeapAlloc dt=28 heapalloc_value=114592896 +GCSweepBegin dt=227 stack=27 +EventBatch gen=3 m=169411 time=28114950895719 size=370 +ProcStatus dt=1 p=21 pstatus=1 +GoStatus dt=5 g=85 m=169411 gstatus=2 +GCMarkAssistActive dt=1 g=85 +GCMarkAssistEnd dt=3 +HeapAlloc dt=44 heapalloc_value=190299584 +GoStop dt=38 reason_string=16 stack=4 +GoStart dt=20 g=85 g_seq=1 +GCMarkAssistBegin dt=119 stack=3 +GoStop dt=4468 reason_string=20 stack=9 +GoStart dt=15 g=85 g_seq=2 +GoStop dt=1589 reason_string=20 stack=9 +GoStart dt=8 g=85 g_seq=3 +GCMarkAssistEnd dt=2892 +HeapAlloc dt=33 heapalloc_value=191733184 +GCMarkAssistBegin dt=98 stack=3 +GoStop dt=2309 reason_string=20 stack=9 +GoStart dt=10 g=95 g_seq=3 +GoBlock dt=153 reason_string=13 stack=11 +GoStart dt=5 g=85 g_seq=4 +GoBlock dt=18 reason_string=13 stack=11 +GoUnblock dt=3925 g=58 g_seq=13 stack=0 +GoStart dt=8 g=58 g_seq=14 +GoLabel dt=3 label_string=4 +GoBlock dt=106 reason_string=15 stack=5 +ProcStop dt=1275 +ProcStart dt=21 p=21 p_seq=1 +ProcStop dt=1335 +ProcStart dt=14 p=21 p_seq=2 +GoUnblock dt=1349 g=14 g_seq=9 stack=0 +GoStart dt=8 g=14 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=255 reason_string=15 stack=5 +GoUnblock dt=2226 g=70 g_seq=9 stack=0 +GoStart dt=8 g=70 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=398 reason_string=15 stack=5 +GoUnblock dt=8 g=70 g_seq=11 stack=0 +GoStart dt=6 g=70 g_seq=12 +GoLabel dt=1 label_string=2 +GoBlock dt=8210 reason_string=15 stack=5 +GoUnblock dt=12 g=70 g_seq=13 stack=0 +GoStart dt=5 g=70 g_seq=14 +GoLabel dt=2 label_string=2 +GoBlock dt=2354 reason_string=15 stack=5 +GoUnblock dt=93 g=72 g_seq=47 stack=0 +GoStart dt=9 g=72 g_seq=48 +GoLabel dt=1 label_string=4 +GoBlock dt=27 reason_string=15 stack=5 +GoUnblock dt=220 g=72 g_seq=49 stack=0 +GoStart dt=7 g=72 g_seq=50 +GoLabel dt=1 label_string=2 +GoBlock dt=20 reason_string=15 stack=5 +ProcStop dt=61 +ProcStart dt=16474 p=33 p_seq=2 +GoStart dt=3475 g=107 g_seq=4 +GCMarkAssistEnd dt=9 +HeapAlloc dt=52 heapalloc_value=196041168 +GoStop dt=5585 reason_string=16 stack=6 +GoStart dt=15 g=107 g_seq=5 +GCMarkAssistBegin dt=91 stack=3 +GoBlock dt=34 reason_string=10 stack=18 +ProcStop dt=55 +ProcStart dt=1514 p=33 p_seq=3 +ProcStop dt=41 +ProcStart dt=12390 p=8 p_seq=1 +GoStart dt=166 g=100 g_seq=7 +GCMarkAssistEnd dt=5 +HeapAlloc dt=51 heapalloc_value=111353984 +GCSweepBegin dt=133 stack=28 +GCSweepEnd dt=18 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=112029568 +HeapAlloc dt=68 heapalloc_value=112301312 +HeapAlloc dt=120 heapalloc_value=112739712 +HeapAlloc dt=116 heapalloc_value=113221760 +HeapAlloc dt=53 heapalloc_value=113380224 +HeapAlloc dt=115 heapalloc_value=113768832 +HeapAlloc dt=66 heapalloc_value=114026880 +HeapAlloc dt=127 heapalloc_value=114403328 +GCSweepBegin dt=47 stack=28 +GCSweepEnd dt=10 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=114503936 +HeapAlloc dt=67 heapalloc_value=114651264 +GCSweepBegin dt=299 stack=27 +EventBatch gen=3 m=169409 time=28114950894853 size=224 +ProcStatus dt=2 p=29 pstatus=1 +GoStatus dt=3 g=126 m=169409 gstatus=2 +HeapAlloc dt=3 heapalloc_value=189824448 +GCMarkAssistBegin dt=163 stack=3 +GoStop dt=1609 reason_string=20 stack=9 +GoStart dt=26 g=98 g_seq=2 +GCMarkAssistBegin dt=17 stack=3 +GCMarkAssistEnd dt=7751 +HeapAlloc dt=77 heapalloc_value=191675840 +GoStop dt=39 reason_string=16 stack=6 +GoStart dt=20 g=116 g_seq=4 +GoBlock dt=302 reason_string=13 stack=11 +GoUnblock dt=4886 g=51 g_seq=13 stack=0 +GoStart dt=8 g=51 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=2058 reason_string=15 stack=5 +GoUnblock dt=11 g=51 g_seq=15 stack=0 +GoStart dt=6 g=51 g_seq=16 +GoLabel dt=3 label_string=2 +GoBlock dt=2936 reason_string=15 stack=5 +GoUnblock dt=35 g=58 g_seq=21 stack=0 +GoStart dt=6 g=58 g_seq=22 +GoLabel dt=3 label_string=2 +GoBlock dt=7995 reason_string=15 stack=5 +GoUnblock dt=20 g=68 g_seq=9 stack=0 +GoStart dt=6 g=68 g_seq=10 +GoLabel dt=3 label_string=2 +GoBlock dt=92 reason_string=15 stack=5 +GoUnblock dt=8 g=68 g_seq=11 stack=0 +GoStart dt=1 g=68 g_seq=12 +GoLabel dt=1 label_string=2 +GoBlock dt=7039 reason_string=15 stack=5 +ProcStop dt=54 +ProcStart dt=14204 p=3 p_seq=1 +GoStart dt=213 g=94 g_seq=7 +GCMarkAssistBegin dt=29 stack=3 +GoBlock dt=62 reason_string=13 stack=11 +GoStart dt=20 g=124 g_seq=4 +GCMarkAssistEnd dt=6 +GCMarkAssistBegin dt=38 stack=3 +GCMarkAssistEnd dt=98 +HeapAlloc dt=118 heapalloc_value=193911512 +HeapAlloc dt=123 heapalloc_value=194116312 +HeapAlloc dt=352 heapalloc_value=194616024 +GoStop dt=3095 reason_string=16 stack=6 +GoStart dt=26 g=110 g_seq=4 +GCMarkAssistEnd dt=6 +HeapAlloc dt=30 heapalloc_value=195508952 +GoStop dt=4300 reason_string=16 stack=6 +GoStart dt=65 g=110 g_seq=5 +GCMarkAssistBegin dt=10 stack=3 +GoBlock dt=46 reason_string=10 stack=18 +ProcStop dt=124 +EventBatch gen=3 m=169408 time=28114950896863 size=856 +ProcStatus dt=1 p=22 pstatus=1 +GoStatus dt=2 g=105 m=169408 gstatus=2 +GCMarkAssistActive dt=1 g=105 +GCMarkAssistEnd dt=2 +HeapAlloc dt=22 heapalloc_value=190512576 +HeapAlloc dt=94 heapalloc_value=190537152 +GCMarkAssistBegin dt=18 stack=3 +GCMarkAssistEnd dt=1243 +HeapAlloc dt=34 heapalloc_value=190741952 +GCMarkAssistBegin dt=36 stack=3 +GCMarkAssistEnd dt=4423 +HeapAlloc dt=22 heapalloc_value=191413696 +GoStop dt=23 reason_string=16 stack=4 +GoStart dt=15 g=105 g_seq=1 +GCMarkAssistBegin dt=57 stack=3 +GoStop dt=662 reason_string=20 stack=9 +GoStart dt=12 g=105 g_seq=2 +GoStop dt=4139 reason_string=20 stack=9 +GoStart dt=11 g=105 g_seq=3 +GoStop dt=4306 reason_string=20 stack=9 +GoStart dt=15 g=105 g_seq=4 +GoBlock dt=21 reason_string=13 stack=11 +GoUnblock dt=2669 g=58 g_seq=19 stack=0 +GoStart dt=5 g=58 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=90 reason_string=15 stack=5 +GoUnblock dt=28 g=51 g_seq=17 stack=0 +GoStart dt=5 g=51 g_seq=18 +GoLabel dt=1 label_string=2 +GoBlock dt=5245 reason_string=15 stack=5 +GoUnblock dt=68 g=51 g_seq=19 stack=0 +GoStart dt=8 g=51 g_seq=20 +GoLabel dt=1 label_string=4 +GoBlock dt=14 reason_string=15 stack=5 +GoUnblock dt=6 g=51 g_seq=21 stack=0 +GoStart dt=1 g=51 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=7035 reason_string=15 stack=5 +GoUnblock dt=13 g=51 g_seq=23 stack=0 +GoStart dt=4 g=51 g_seq=24 +GoLabel dt=2 label_string=2 +GoUnblock dt=188 g=116 g_seq=5 stack=12 +GoBlock dt=65 reason_string=15 stack=5 +GoUnblock dt=9 g=51 g_seq=25 stack=0 +GoStart dt=2 g=51 g_seq=26 +GoLabel dt=1 label_string=2 +GoBlock dt=170 reason_string=15 stack=5 +GoUnblock dt=15 g=51 g_seq=27 stack=0 +GoStart dt=6 g=51 g_seq=28 +GoLabel dt=1 label_string=2 +GoBlock dt=33 reason_string=15 stack=5 +GoUnblock dt=7 g=51 g_seq=29 stack=0 +GoStart dt=6 g=51 g_seq=30 +GoLabel dt=1 label_string=2 +GoBlock dt=159 reason_string=15 stack=5 +GoUnblock dt=8 g=51 g_seq=31 stack=0 +GoStart dt=3 g=51 g_seq=32 +GoLabel dt=1 label_string=2 +GoBlock dt=124 reason_string=15 stack=5 +ProcStop dt=79 +ProcStart dt=18 p=22 p_seq=1 +GoUnblock dt=4 g=29 g_seq=21 stack=0 +GoStart dt=4 g=29 g_seq=22 +GoLabel dt=1 label_string=4 +GoBlock dt=28 reason_string=15 stack=5 +ProcStop dt=45 +ProcStart dt=12 p=22 p_seq=2 +GoUnblock dt=2 g=29 g_seq=23 stack=0 +GoStart dt=1 g=29 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=19 reason_string=15 stack=5 +GoUnblock dt=45 g=29 g_seq=25 stack=0 +GoStart dt=1 g=29 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=151 reason_string=15 stack=5 +GoUnblock dt=14 g=52 g_seq=35 stack=0 +GoStart dt=6 g=52 g_seq=36 +GoLabel dt=1 label_string=2 +GoBlock dt=13 reason_string=15 stack=5 +GoUnblock dt=4 g=52 g_seq=37 stack=0 +GoStart dt=3 g=52 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=127 reason_string=15 stack=5 +GoUnblock dt=7 g=52 g_seq=39 stack=0 +GoStart dt=1 g=52 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=11 reason_string=15 stack=5 +GoUnblock dt=6 g=52 g_seq=41 stack=0 +GoStart dt=2 g=52 g_seq=42 +GoLabel dt=1 label_string=2 +GoBlock dt=4594 reason_string=15 stack=5 +ProcStop dt=42 +ProcStart dt=1703 p=27 p_seq=42 +GoUnblock dt=17 g=22 g_seq=45 stack=0 +GoStart dt=283 g=22 g_seq=46 +GoLabel dt=2 label_string=2 +GoUnblock dt=103 g=96 g_seq=3 stack=12 +GoUnblock dt=95 g=121 g_seq=5 stack=12 +GoUnblock dt=5 g=126 g_seq=2 stack=12 +GoUnblock dt=529 g=115 g_seq=3 stack=12 +GoBlock dt=552 reason_string=15 stack=5 +GoUnblock dt=31 g=22 g_seq=47 stack=0 +GoStart dt=4 g=22 g_seq=48 +GoLabel dt=1 label_string=2 +GoUnblock dt=763 g=90 g_seq=3 stack=12 +GoBlock dt=39 reason_string=15 stack=5 +GoUnblock dt=12 g=22 g_seq=49 stack=0 +GoStart dt=4 g=22 g_seq=50 +GoLabel dt=1 label_string=2 +GoBlock dt=806 reason_string=15 stack=5 +GoStart dt=18 g=115 g_seq=4 +GCMarkAssistEnd dt=8 +HeapAlloc dt=834 heapalloc_value=192494296 +GCMarkAssistBegin dt=33 stack=3 +GoStop dt=622 reason_string=20 stack=9 +GoUnblock dt=15 g=14 g_seq=44 stack=0 +GoStart dt=5 g=14 g_seq=45 +GoLabel dt=1 label_string=2 +GoBlock dt=1768 reason_string=15 stack=5 +GoUnblock dt=11 g=14 g_seq=46 stack=0 +GoStart dt=4 g=14 g_seq=47 +GoLabel dt=1 label_string=2 +GoBlock dt=20 reason_string=15 stack=5 +GoUnblock dt=10 g=14 g_seq=48 stack=0 +GoStart dt=636 g=14 g_seq=49 +GoLabel dt=1 label_string=2 +GoBlock dt=55 reason_string=15 stack=5 +GoUnblock dt=18 g=14 g_seq=50 stack=0 +GoStart dt=3 g=14 g_seq=51 +GoLabel dt=1 label_string=2 +GoBlock dt=46 reason_string=15 stack=5 +GoUnblock dt=15 g=14 g_seq=52 stack=0 +GoStart dt=4 g=14 g_seq=53 +GoLabel dt=1 label_string=2 +GoBlock dt=26 reason_string=15 stack=5 +GoUnblock dt=29 g=70 g_seq=23 stack=0 +GoStart dt=5 g=70 g_seq=24 +GoLabel dt=1 label_string=2 +GoBlock dt=15 reason_string=15 stack=5 +GoStart dt=30 g=94 g_seq=6 +GCMarkAssistEnd dt=5 +HeapAlloc dt=37 heapalloc_value=192699096 +GoStop dt=34 reason_string=16 stack=6 +GoUnblock dt=9 g=70 g_seq=25 stack=0 +GoStart dt=3 g=70 g_seq=26 +GoLabel dt=1 label_string=2 +GoUnblock dt=190 g=98 g_seq=7 stack=12 +GoUnblock dt=6 g=91 g_seq=1 stack=12 +GoUnblock dt=7 g=123 g_seq=6 stack=12 +GoUnblock dt=5 g=100 g_seq=3 stack=12 +GoUnblock dt=3 g=102 g_seq=3 stack=12 +GoUnblock dt=3 g=103 g_seq=4 stack=12 +GoUnblock dt=5 g=117 g_seq=3 stack=12 +GoBlock dt=45 reason_string=15 stack=5 +GoUnblock dt=8 g=70 g_seq=27 stack=0 +GoStart dt=1 g=70 g_seq=28 +GoLabel dt=1 label_string=2 +GoUnblock dt=1939 g=111 g_seq=7 stack=12 +GoUnblock dt=10 g=101 g_seq=5 stack=12 +GoBlock dt=23 reason_string=15 stack=5 +GoStart dt=15 g=98 g_seq=8 +GCMarkAssistEnd dt=8 +HeapAlloc dt=57 heapalloc_value=193960664 +GCMarkAssistBegin dt=83 stack=3 +GoBlock dt=26 reason_string=13 stack=11 +GoStart dt=7 g=91 g_seq=2 +GCMarkAssistEnd dt=6 +HeapAlloc dt=47 heapalloc_value=194296536 +GCMarkAssistBegin dt=103 stack=3 +GoBlock dt=118 reason_string=13 stack=11 +GoStart dt=20 g=123 g_seq=7 +GCMarkAssistEnd dt=4 +HeapAlloc dt=448 heapalloc_value=195058392 +GoStop dt=6487 reason_string=16 stack=6 +GoStart dt=27 g=123 g_seq=8 +GCMarkAssistBegin dt=10 stack=3 +GoBlock dt=32 reason_string=10 stack=18 +ProcStop dt=78 +ProcStart dt=16845 p=9 p_seq=1 +GoStart dt=21 g=127 g_seq=10 +GCMarkAssistEnd dt=11 +GCSweepBegin dt=37 stack=28 +GCSweepEnd dt=17 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=7 heapalloc_value=110613376 +HeapAlloc dt=77 heapalloc_value=110956160 +HeapAlloc dt=127 heapalloc_value=111501184 +HeapAlloc dt=150 heapalloc_value=112133376 +HeapAlloc dt=103 heapalloc_value=112487168 +HeapAlloc dt=158 heapalloc_value=113166976 +GCSweepBegin dt=50 stack=28 +GCSweepEnd dt=32 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=6 heapalloc_value=113407616 +HeapAlloc dt=173 heapalloc_value=114067840 +HeapAlloc dt=153 heapalloc_value=114430208 +GCSweepBegin dt=35 stack=28 +GCSweepEnd dt=4 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=4 heapalloc_value=114551936 +GCSweepBegin dt=1034 stack=27 +EventBatch gen=3 m=169407 time=28114950901555 size=528 +ProcStatus dt=2 p=4 pstatus=1 +GoStatus dt=1 g=72 m=169407 gstatus=2 +GoBlock dt=7 reason_string=15 stack=5 +GoUnblock dt=1446 g=72 g_seq=3 stack=0 +GoStart dt=9 g=72 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=394 reason_string=15 stack=5 +GoStart dt=26 g=106 g_seq=3 +GoBlock dt=149 reason_string=13 stack=11 +GoUnblock dt=2557 g=72 g_seq=5 stack=0 +GoStart dt=8 g=72 g_seq=6 +GoLabel dt=1 label_string=4 +GoBlock dt=44 reason_string=15 stack=5 +GoUnblock dt=13 g=72 g_seq=7 stack=0 +GoStart dt=6 g=72 g_seq=8 +GoLabel dt=5 label_string=2 +GoBlock dt=1622 reason_string=15 stack=5 +GoUnblock dt=9 g=72 g_seq=9 stack=0 +GoStart dt=6 g=72 g_seq=10 +GoLabel dt=1 label_string=2 +GoUnblock dt=165 g=87 g_seq=2 stack=12 +GoBlock dt=854 reason_string=15 stack=5 +GoUnblock dt=9 g=72 g_seq=11 stack=0 +GoStart dt=4 g=72 g_seq=12 +GoLabel dt=1 label_string=2 +GoBlock dt=398 reason_string=15 stack=5 +GoUnblock dt=20 g=72 g_seq=13 stack=0 +GoStart dt=5 g=72 g_seq=14 +GoLabel dt=1 label_string=2 +GoBlock dt=1475 reason_string=15 stack=5 +GoStart dt=1158 g=93 g_seq=2 +GoStatus dt=24 g=94 m=18446744073709551615 gstatus=4 +GoUnblock dt=5 g=94 g_seq=1 stack=10 +GCMarkAssistBegin dt=19 stack=3 +GoBlock dt=235 reason_string=13 stack=11 +GoStart dt=9 g=94 g_seq=2 +GoStatus dt=18 g=100 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=100 g_seq=1 stack=10 +GCMarkAssistBegin dt=16 stack=3 +GoStop dt=7669 reason_string=20 stack=9 +GoStart dt=9 g=94 g_seq=3 +GoStop dt=5028 reason_string=20 stack=9 +GoUnblock dt=76 g=23 g_seq=39 stack=0 +GoStart dt=4 g=23 g_seq=40 +GoLabel dt=1 label_string=4 +GoBlock dt=464 reason_string=15 stack=5 +GoUnblock dt=67 g=23 g_seq=41 stack=0 +GoStart dt=151 g=23 g_seq=42 +GoLabel dt=2 label_string=4 +GoBlock dt=3280 reason_string=15 stack=5 +GoStart dt=35 g=113 g_seq=6 +GCMarkAssistEnd dt=7 +GCMarkAssistBegin dt=65 stack=3 +GoBlock dt=63 reason_string=13 stack=11 +ProcStop dt=162 +ProcStart dt=22113 p=24 p_seq=4 +GoStart dt=228 g=111 g_seq=8 +GCMarkAssistEnd dt=11 +HeapAlloc dt=64 heapalloc_value=196401616 +GoStop dt=6120 reason_string=16 stack=6 +GoStart dt=26 g=111 g_seq=9 +GCMarkAssistBegin dt=15 stack=3 +GoBlock dt=35 reason_string=10 stack=18 +ProcStop dt=128 +ProcStart dt=7783 p=1 p_seq=3 +GoStart dt=191 g=87 g_seq=8 +GCMarkAssistEnd dt=9 +GCSweepBegin dt=33 stack=28 +GCSweepEnd dt=16 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=4 heapalloc_value=103833248 +GCSweepBegin dt=56 stack=27 +GCSweepEnd dt=1508 swept_value=4194304 reclaimed_value=3072000 +HeapAlloc dt=33 heapalloc_value=105692064 +HeapAlloc dt=115 heapalloc_value=105976736 +HeapAlloc dt=44 heapalloc_value=106034080 +HeapAlloc dt=109 heapalloc_value=106332320 +HeapAlloc dt=95 heapalloc_value=106715424 +HeapAlloc dt=80 heapalloc_value=106958496 +HeapAlloc dt=97 heapalloc_value=107330592 +HeapAlloc dt=56 heapalloc_value=107460384 +HeapAlloc dt=117 heapalloc_value=107811360 +HeapAlloc dt=62 heapalloc_value=108141856 +HeapAlloc dt=115 heapalloc_value=108472352 +HeapAlloc dt=103 heapalloc_value=108710048 +GCSweepBegin dt=51 stack=28 +GCSweepEnd dt=11 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=4 heapalloc_value=108832928 +HeapAlloc dt=51 heapalloc_value=109134624 +HeapAlloc dt=100 heapalloc_value=109470496 +HeapAlloc dt=98 heapalloc_value=109831200 +HeapAlloc dt=69 heapalloc_value=110087968 +HeapAlloc dt=117 heapalloc_value=110388096 +HeapAlloc dt=150 heapalloc_value=111005312 +HeapAlloc dt=140 heapalloc_value=111509376 +HeapAlloc dt=55 heapalloc_value=111773568 +HeapAlloc dt=105 heapalloc_value=112162048 +GCSweepBegin dt=85 stack=28 +GCSweepEnd dt=8 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=112560896 +HeapAlloc dt=68 heapalloc_value=112816768 +HeapAlloc dt=47 heapalloc_value=112988800 +HeapAlloc dt=122 heapalloc_value=113464960 +HeapAlloc dt=150 heapalloc_value=114008448 +GCSweepBegin dt=885 stack=27 +EventBatch gen=3 m=169406 time=28114950897134 size=117 +ProcStatus dt=3 p=6 pstatus=1 +GoStatus dt=5 g=52 m=169406 gstatus=2 +GoBlock dt=14 reason_string=15 stack=5 +GoUnblock dt=16 g=52 g_seq=1 stack=0 +GoStart dt=5 g=52 g_seq=2 +GoLabel dt=1 label_string=2 +GoBlock dt=3752 reason_string=15 stack=5 +GoUnblock dt=21 g=52 g_seq=3 stack=0 +GoStart dt=7 g=52 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=4444 reason_string=15 stack=5 +GoUnblock dt=12 g=52 g_seq=5 stack=0 +GoStart dt=7 g=52 g_seq=6 +GoLabel dt=1 label_string=2 +GoBlock dt=5071 reason_string=15 stack=5 +GoUnblock dt=15 g=52 g_seq=7 stack=0 +GoStart dt=6 g=52 g_seq=8 +GoLabel dt=2 label_string=2 +GoBlock dt=2302 reason_string=15 stack=5 +GoUnblock dt=14 g=52 g_seq=9 stack=0 +GoStart dt=6 g=52 g_seq=10 +GoLabel dt=1 label_string=2 +GoBlock dt=32 reason_string=15 stack=5 +GoUnblock dt=9 g=52 g_seq=11 stack=0 +GoStart dt=6 g=52 g_seq=12 +GoLabel dt=1 label_string=2 +GoBlock dt=22 reason_string=15 stack=5 +ProcStop dt=35 +EventBatch gen=3 m=169405 time=28114950903578 size=119 +ProcStatus dt=2 p=15 pstatus=1 +GoStatus dt=4 g=53 m=169405 gstatus=2 +GoBlock dt=8 reason_string=15 stack=5 +GoUnblock dt=5238 g=25 g_seq=7 stack=0 +GoStart dt=7 g=25 g_seq=8 +GoLabel dt=1 label_string=4 +GoBlock dt=49 reason_string=15 stack=5 +GoUnblock dt=1111 g=58 g_seq=11 stack=0 +GoStart dt=6 g=58 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=158 reason_string=15 stack=5 +GoStart dt=3143 g=100 g_seq=2 +GoStatus dt=20 g=109 m=18446744073709551615 gstatus=4 +GoUnblock dt=7 g=109 g_seq=1 stack=10 +GCMarkAssistBegin dt=17 stack=3 +GoBlock dt=2307 reason_string=13 stack=11 +GoUnblock dt=2192 g=14 g_seq=13 stack=0 +GoStart dt=4 g=14 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=1366 reason_string=15 stack=5 +GoUnblock dt=68 g=23 g_seq=21 stack=0 +GoStart dt=4 g=23 g_seq=22 +GoLabel dt=1 label_string=4 +GoBlock dt=21 reason_string=15 stack=5 +ProcStop dt=3159 +EventBatch gen=3 m=169404 time=28114950896316 size=116 +ProcStatus dt=1 p=5 pstatus=1 +GoStatus dt=2 g=14 m=169404 gstatus=2 +GoBlock dt=5 reason_string=15 stack=5 +GoUnblock dt=1436 g=67 g_seq=3 stack=0 +GoStart dt=217 g=67 g_seq=4 +GoLabel dt=3 label_string=4 +GoBlock dt=1945 reason_string=15 stack=5 +GoStart dt=23 g=121 g_seq=3 +GoStop dt=570 reason_string=20 stack=9 +GoStart dt=14 g=121 g_seq=4 +GoBlock dt=1389 reason_string=13 stack=11 +GoUnblock dt=13 g=51 g_seq=3 stack=0 +GoStart dt=7 g=51 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=1439 reason_string=15 stack=5 +GoUnblock dt=17 g=14 g_seq=5 stack=0 +GoStart dt=5 g=14 g_seq=6 +GoLabel dt=2 label_string=2 +GoBlock dt=11474 reason_string=15 stack=5 +GoStart dt=4166 g=109 g_seq=3 +GoBlock dt=39 reason_string=13 stack=11 +GoStart dt=20 g=119 g_seq=4 +GCMarkAssistEnd dt=7 +HeapAlloc dt=68 heapalloc_value=191921600 +GCMarkAssistBegin dt=69 stack=3 +GoBlock dt=23 reason_string=13 stack=11 +ProcStop dt=59 +EventBatch gen=3 m=169402 time=28114950895074 size=135 +ProcStatus dt=2 p=9 pstatus=1 +GoStatus dt=2 g=25 m=169402 gstatus=2 +GoBlock dt=14 reason_string=15 stack=5 +GoStart dt=54 g=98 g_seq=1 +GCMarkAssistBegin dt=99 stack=3 +GCMarkAssistEnd dt=1187 +HeapAlloc dt=68 heapalloc_value=190463424 +GoStop dt=53 reason_string=16 stack=6 +GoStart dt=10 g=82 g_seq=1 +GCMarkAssistBegin dt=82 stack=3 +GoStop dt=2699 reason_string=20 stack=9 +GoStart dt=13 g=107 g_seq=2 +GCMarkAssistEnd dt=7 +GCMarkAssistBegin dt=49 stack=3 +GoBlock dt=852 reason_string=13 stack=11 +GoStart dt=29 g=90 g_seq=2 +GCMarkAssistEnd dt=3 +HeapAlloc dt=36 heapalloc_value=191233472 +GCMarkAssistBegin dt=825 stack=3 +GoBlock dt=392 reason_string=13 stack=11 +GoUnblock dt=21 g=67 g_seq=5 stack=0 +GoStart dt=5 g=67 g_seq=6 +GoLabel dt=1 label_string=2 +GoBlock dt=8638 reason_string=15 stack=5 +GoUnblock dt=9 g=67 g_seq=7 stack=0 +GoStart dt=4 g=67 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=145 reason_string=15 stack=5 +GoUnblock dt=14 g=67 g_seq=9 stack=0 +GoStart dt=5 g=67 g_seq=10 +GoLabel dt=1 label_string=2 +GoBlock dt=7067 reason_string=15 stack=5 +ProcStop dt=23 +EventBatch gen=3 m=169401 time=28114950894770 size=505 +ProcStatus dt=1 p=8 pstatus=1 +GoStatus dt=1 g=130 m=169401 gstatus=2 +ProcsChange dt=124 procs_value=48 stack=1 +GCActive dt=3 gc_seq=4 +HeapAlloc dt=600 heapalloc_value=190152128 +HeapAlloc dt=16 heapalloc_value=190160320 +HeapAlloc dt=11095 heapalloc_value=191741376 +HeapAlloc dt=179 heapalloc_value=191749568 +HeapAlloc dt=14244 heapalloc_value=192011712 +HeapAlloc dt=292 heapalloc_value=192019904 +HeapAlloc dt=244 heapalloc_value=192028096 +HeapAlloc dt=3225 heapalloc_value=192036288 +HeapAlloc dt=39 heapalloc_value=192044192 +HeapAlloc dt=60 heapalloc_value=192052000 +HeapAlloc dt=462 heapalloc_value=192060192 +HeapAlloc dt=85 heapalloc_value=192068384 +HeapAlloc dt=341 heapalloc_value=192076576 +HeapAlloc dt=314 heapalloc_value=192142112 +GoStop dt=8367 reason_string=16 stack=14 +GoUnblock dt=274 g=30 g_seq=27 stack=0 +GoStart dt=6 g=30 g_seq=28 +GoLabel dt=1 label_string=2 +GoBlock dt=312 reason_string=15 stack=5 +GoUnblock dt=403 g=30 g_seq=29 stack=0 +GoStart dt=4 g=30 g_seq=30 +GoLabel dt=1 label_string=2 +GoBlock dt=773 reason_string=15 stack=5 +GoUnblock dt=7 g=30 g_seq=31 stack=0 +GoStart dt=3 g=30 g_seq=32 +GoLabel dt=1 label_string=2 +GoBlock dt=8 reason_string=15 stack=5 +GoStart dt=14 g=112 g_seq=4 +GCMarkAssistEnd dt=6 +HeapAlloc dt=45 heapalloc_value=192297760 +GCMarkAssistBegin dt=107 stack=3 +GoStop dt=897 reason_string=20 stack=9 +GoUnblock dt=15 g=70 g_seq=19 stack=0 +GoStart dt=5 g=70 g_seq=20 +GoLabel dt=1 label_string=2 +GoUnblock dt=1479 g=105 g_seq=5 stack=12 +GoBlock dt=2280 reason_string=15 stack=5 +GoUnblock dt=12 g=70 g_seq=21 stack=0 +GoStart dt=5 g=70 g_seq=22 +GoLabel dt=2 label_string=2 +GoBlock dt=1253 reason_string=15 stack=5 +GoUnblock dt=23 g=71 g_seq=35 stack=0 +GoStart dt=8 g=71 g_seq=36 +GoLabel dt=2 label_string=2 +GoBlock dt=26 reason_string=15 stack=5 +GoUnblock dt=6 g=71 g_seq=37 stack=0 +GoStart dt=3 g=71 g_seq=38 +GoLabel dt=1 label_string=2 +GoBlock dt=9 reason_string=15 stack=5 +GoUnblock dt=3 g=71 g_seq=39 stack=0 +GoStart dt=2 g=71 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=21 reason_string=15 stack=5 +GoUnblock dt=3 g=71 g_seq=41 stack=0 +GoStart dt=1 g=71 g_seq=42 +GoLabel dt=1 label_string=2 +GoUnblock dt=82 g=109 g_seq=4 stack=12 +GoUnblock dt=6 g=106 g_seq=4 stack=12 +GoUnblock dt=103 g=111 g_seq=4 stack=12 +GoUnblock dt=5 g=112 g_seq=6 stack=12 +GoUnblock dt=6 g=96 g_seq=5 stack=12 +GoUnblock dt=4 g=119 g_seq=5 stack=12 +GoUnblock dt=6 g=122 g_seq=1 stack=12 +GoUnblock dt=11 g=97 g_seq=5 stack=12 +GoUnblock dt=4 g=107 g_seq=3 stack=12 +GoUnblock dt=106 g=92 g_seq=3 stack=12 +GoUnblock dt=4 g=116 g_seq=9 stack=12 +GoUnblock dt=5 g=82 g_seq=8 stack=12 +GoBlock dt=9 reason_string=15 stack=5 +GoStart dt=12 g=111 g_seq=5 +GCMarkAssistEnd dt=5 +HeapAlloc dt=22 heapalloc_value=192797400 +GCMarkAssistBegin dt=75 stack=3 +GoStop dt=22 reason_string=20 stack=9 +GoUnblock dt=11 g=25 g_seq=53 stack=0 +GoStart dt=4 g=25 g_seq=54 +GoLabel dt=1 label_string=2 +GoUnblock dt=1354 g=95 g_seq=4 stack=12 +GoUnblock dt=9 g=90 g_seq=6 stack=12 +GoUnblock dt=6 g=113 g_seq=9 stack=12 +GoUnblock dt=3 g=89 g_seq=6 stack=12 +GoBlock dt=30 reason_string=15 stack=5 +GoStart dt=10 g=112 g_seq=7 +GCMarkAssistEnd dt=5 +GCMarkAssistBegin dt=28 stack=3 +GoBlock dt=587 reason_string=13 stack=11 +GoStart dt=6 g=116 g_seq=10 +GCMarkAssistEnd dt=5 +HeapAlloc dt=54 heapalloc_value=194337496 +GCMarkAssistBegin dt=51 stack=3 +GoBlock dt=21 reason_string=13 stack=11 +GoStart dt=8 g=82 g_seq=9 +GCMarkAssistEnd dt=6 +HeapAlloc dt=63 heapalloc_value=194525912 +GCMarkAssistBegin dt=51 stack=3 +GoBlock dt=45 reason_string=13 stack=11 +GoStart dt=22 g=95 g_seq=5 +GCMarkAssistEnd dt=6 +HeapAlloc dt=1508 heapalloc_value=195394264 +GoStop dt=6034 reason_string=16 stack=6 +GoStart dt=48 g=95 g_seq=6 +GCMarkAssistBegin dt=18 stack=3 +GoBlock dt=48 reason_string=10 stack=18 +ProcStop dt=85 +ProcStart dt=20619 p=17 p_seq=1 +GoStart dt=1507 g=130 g_seq=7 +EventBatch gen=3 m=169400 time=28114950894819 size=671 +ProcStatus dt=1 p=12 pstatus=1 +GoStatus dt=2 g=112 m=169400 gstatus=2 +GCMarkAssistBegin dt=120 stack=3 +GCMarkAssistEnd dt=3298 +HeapAlloc dt=41 heapalloc_value=190758336 +GCMarkAssistBegin dt=29 stack=3 +GoStop dt=2271 reason_string=20 stack=9 +GoStart dt=14 g=112 g_seq=1 +GoStop dt=569 reason_string=20 stack=9 +GoUnblock dt=2436 g=54 g_seq=1 stack=0 +GoStart dt=18 g=54 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=31 reason_string=15 stack=5 +GoUnblock dt=5090 g=57 g_seq=13 stack=0 +GoStart dt=6 g=57 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=734 reason_string=15 stack=5 +GoUnblock dt=4144 g=71 g_seq=15 stack=0 +GoStart dt=5 g=71 g_seq=16 +GoLabel dt=1 label_string=4 +GoUnblock dt=415 g=111 g_seq=2 stack=12 +GoBlock dt=5674 reason_string=15 stack=5 +GoUnblock dt=9 g=71 g_seq=17 stack=0 +GoStart dt=5 g=71 g_seq=18 +GoLabel dt=1 label_string=2 +GoUnblock dt=693 g=83 g_seq=3 stack=12 +GoBlock dt=4708 reason_string=15 stack=5 +GoUnblock dt=14 g=71 g_seq=19 stack=0 +GoStart dt=6 g=71 g_seq=20 +GoLabel dt=3 label_string=2 +GoBlock dt=1294 reason_string=15 stack=5 +GoUnblock dt=11 g=71 g_seq=21 stack=0 +GoStart dt=4 g=71 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=2434 reason_string=15 stack=5 +GoUnblock dt=8 g=71 g_seq=23 stack=0 +GoStart dt=3 g=71 g_seq=24 +GoLabel dt=1 label_string=2 +GoBlock dt=4227 reason_string=15 stack=5 +ProcStop dt=41 +ProcStart dt=3260 p=12 p_seq=1 +GoUnblock dt=16 g=30 g_seq=33 stack=0 +GoStart dt=143 g=30 g_seq=34 +GoLabel dt=1 label_string=2 +GoUnblock dt=553 g=89 g_seq=3 stack=12 +GoUnblock dt=971 g=127 g_seq=3 stack=12 +GoBlock dt=39 reason_string=15 stack=5 +GoStart dt=21 g=89 g_seq=4 +GCMarkAssistEnd dt=10 +HeapAlloc dt=1100 heapalloc_value=192510680 +GoStop dt=24 reason_string=16 stack=6 +GoUnblock dt=12 g=22 g_seq=51 stack=0 +GoStart dt=5 g=22 g_seq=52 +GoLabel dt=3 label_string=2 +GoBlock dt=1678 reason_string=15 stack=5 +GoUnblock dt=13 g=22 g_seq=53 stack=0 +GoStart dt=277 g=22 g_seq=54 +GoLabel dt=3 label_string=2 +GoBlock dt=960 reason_string=15 stack=5 +GoUnblock dt=8 g=22 g_seq=55 stack=0 +GoStart dt=4 g=22 g_seq=56 +GoLabel dt=1 label_string=2 +GoUnblock dt=583 g=99 g_seq=3 stack=12 +GoUnblock dt=5 g=83 g_seq=6 stack=12 +GoUnblock dt=5 g=124 g_seq=3 stack=12 +GoUnblock dt=6 g=105 g_seq=9 stack=12 +GoUnblock dt=1280 g=128 g_seq=3 stack=12 +GoUnblock dt=8 g=101 g_seq=3 stack=12 +GoBlock dt=7 reason_string=15 stack=5 +GoStart dt=11 g=128 g_seq=4 +GCMarkAssistEnd dt=7 +HeapAlloc dt=38 heapalloc_value=193297112 +GCMarkAssistBegin dt=118 stack=3 +GCMarkAssistEnd dt=44 +HeapAlloc dt=21 heapalloc_value=193403608 +GoStop dt=87 reason_string=16 stack=6 +GoStart dt=15 g=101 g_seq=4 +GCMarkAssistEnd dt=5 +HeapAlloc dt=58 heapalloc_value=193608408 +GCMarkAssistBegin dt=92 stack=3 +GoBlock dt=22 reason_string=13 stack=11 +GoStart dt=10 g=128 g_seq=5 +HeapAlloc dt=34 heapalloc_value=193829592 +HeapAlloc dt=166 heapalloc_value=194026200 +HeapAlloc dt=236 heapalloc_value=194419416 +HeapAlloc dt=885 heapalloc_value=195279576 +GoStop dt=6734 reason_string=16 stack=6 +GoUnblock dt=1628 g=130 g_seq=3 stack=0 +GoStart dt=136 g=130 g_seq=4 +HeapAlloc dt=62 heapalloc_value=196532688 +HeapAlloc dt=28 heapalloc_value=196540880 +HeapAlloc dt=22 heapalloc_value=196549072 +HeapAlloc dt=26 heapalloc_value=196557264 +HeapAlloc dt=38 heapalloc_value=196565456 +HeapAlloc dt=51 heapalloc_value=196573648 +GoStop dt=3032 reason_string=16 stack=19 +GoStart dt=10 g=117 g_seq=5 +GCMarkAssistBegin dt=16 stack=3 +GoBlock dt=51 reason_string=10 stack=18 +ProcStop dt=29 +ProcStart dt=9381 p=4 p_seq=2 +GoStart dt=190 g=105 g_seq=16 +GCMarkAssistEnd dt=4 +HeapAlloc dt=76 heapalloc_value=105214112 +HeapAlloc dt=103 heapalloc_value=105517216 +HeapAlloc dt=84 heapalloc_value=105642912 +HeapAlloc dt=85 heapalloc_value=105864096 +GCSweepBegin dt=188 stack=28 +GCSweepEnd dt=17 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=2 heapalloc_value=106376096 +HeapAlloc dt=43 heapalloc_value=106518816 +HeapAlloc dt=43 heapalloc_value=106756384 +HeapAlloc dt=82 heapalloc_value=106978976 +HeapAlloc dt=42 heapalloc_value=107091616 +GCSweepBegin dt=23 stack=28 +GCSweepEnd dt=8 swept_value=8192 reclaimed_value=8192 +HeapAlloc dt=3 heapalloc_value=107310112 +HeapAlloc dt=35 heapalloc_value=107372960 +HeapAlloc dt=65 heapalloc_value=107583264 +HeapAlloc dt=141 heapalloc_value=108018976 +HeapAlloc dt=161 heapalloc_value=108567968 +GCSweepBegin dt=85 stack=28 +GCSweepEnd dt=9 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=4 heapalloc_value=108808352 +HeapAlloc dt=90 heapalloc_value=109241120 +HeapAlloc dt=139 heapalloc_value=109623584 +HeapAlloc dt=162 heapalloc_value=110175008 +HeapAlloc dt=164 heapalloc_value=110769024 +HeapAlloc dt=246 heapalloc_value=111705984 +HeapAlloc dt=187 heapalloc_value=112446208 +HeapAlloc dt=161 heapalloc_value=113148544 +HeapAlloc dt=295 heapalloc_value=114145664 +GCSweepBegin dt=159 stack=28 +GCSweepEnd dt=5 swept_value=8192 reclaimed_value=8192 +HeapAlloc dt=7 heapalloc_value=114588800 +GCSweepBegin dt=48 stack=27 +EventBatch gen=3 m=169398 time=28114950899192 size=165 +ProcStatus dt=1 p=37 pstatus=2 +ProcStart dt=2 p=37 p_seq=1 +GoStatus dt=3261 g=29 m=18446744073709551615 gstatus=4 +GoUnblock dt=6 g=29 g_seq=1 stack=0 +GoStart dt=10 g=29 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=1840 reason_string=15 stack=5 +GoStart dt=16 g=86 g_seq=3 +GoBlock dt=1090 reason_string=13 stack=11 +ProcStop dt=1389 +ProcStart dt=16 p=37 p_seq=2 +GoStart dt=1537 g=84 g_seq=4 +GCMarkAssistEnd dt=7 +HeapAlloc dt=55 heapalloc_value=191847872 +GCMarkAssistBegin dt=85 stack=3 +GoBlock dt=249 reason_string=13 stack=11 +GoUnblock dt=1134 g=58 g_seq=9 stack=0 +GoStart dt=7 g=58 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=27 reason_string=15 stack=5 +GoUnblock dt=2190 g=53 g_seq=9 stack=0 +GoStart dt=8 g=53 g_seq=10 +GoLabel dt=1 label_string=4 +GoBlock dt=21 reason_string=15 stack=5 +GoUnblock dt=2156 g=25 g_seq=13 stack=0 +GoStart dt=4 g=25 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=20 reason_string=15 stack=5 +GoUnblock dt=1089 g=14 g_seq=7 stack=0 +GoStart dt=4 g=14 g_seq=8 +GoLabel dt=1 label_string=4 +GoBlock dt=107 reason_string=15 stack=5 +GoUnblock dt=1081 g=24 g_seq=15 stack=0 +GoStart dt=6 g=24 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=19 reason_string=15 stack=5 +ProcStop dt=1075 +EventBatch gen=3 m=169397 time=28114950897533 size=734 +ProcStatus dt=1 p=25 pstatus=1 +GoStatus dt=2 g=118 m=169397 gstatus=2 +GCMarkAssistActive dt=1 g=118 +GCMarkAssistEnd dt=2 +HeapAlloc dt=37 heapalloc_value=190684608 +GCMarkAssistBegin dt=79 stack=3 +GoBlock dt=1327 reason_string=13 stack=11 +ProcStop dt=4643 +ProcStart dt=23 p=25 p_seq=1 +GoUnblock dt=20 g=53 g_seq=1 stack=0 +GoStart dt=9 g=53 g_seq=2 +GoLabel dt=1 label_string=2 +GoBlock dt=2529 reason_string=15 stack=5 +GoStart dt=3244 g=123 g_seq=2 +GoStatus dt=30 g=97 m=18446744073709551615 gstatus=4 +GoUnblock dt=13 g=97 g_seq=1 stack=10 +GCMarkAssistBegin dt=20 stack=3 +GoStop dt=1976 reason_string=20 stack=9 +GoStart dt=15 g=123 g_seq=3 +GoStop dt=2654 reason_string=20 stack=9 +GoStart dt=12 g=123 g_seq=4 +GoStop dt=2704 reason_string=20 stack=9 +GoUnblock dt=9 g=24 g_seq=17 stack=0 +GoStart dt=4 g=24 g_seq=18 +GoLabel dt=1 label_string=2 +GoBlock dt=4029 reason_string=15 stack=5 +GoUnblock dt=14 g=24 g_seq=19 stack=0 +GoStart dt=4 g=24 g_seq=20 +GoLabel dt=1 label_string=2 +GoBlock dt=534 reason_string=15 stack=5 +GoUnblock dt=4 g=24 g_seq=21 stack=0 +GoStart dt=4 g=24 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=250 reason_string=15 stack=5 +GoUnblock dt=12 g=24 g_seq=23 stack=0 +GoStart dt=4 g=24 g_seq=24 +GoLabel dt=1 label_string=2 +GoBlock dt=22 reason_string=15 stack=5 +ProcStop dt=71 +ProcStart dt=244 p=25 p_seq=2 +ProcStop dt=54 +ProcStart dt=25 p=25 p_seq=3 +GoUnblock dt=8 g=53 g_seq=21 stack=0 +GoStart dt=7 g=53 g_seq=22 +GoLabel dt=1 label_string=4 +GoBlock dt=86 reason_string=15 stack=5 +GoUnblock dt=59 g=56 g_seq=3 stack=0 +GoStart dt=4 g=56 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=6219 reason_string=15 stack=5 +GoUnblock dt=52 g=56 g_seq=5 stack=0 +GoStart dt=4 g=56 g_seq=6 +GoLabel dt=1 label_string=4 +GoBlock dt=98 reason_string=15 stack=5 +GoUnblock dt=61 g=14 g_seq=27 stack=0 +GoStart dt=4 g=14 g_seq=28 +GoLabel dt=1 label_string=4 +GoBlock dt=32 reason_string=15 stack=5 +GoUnblock dt=13 g=14 g_seq=29 stack=0 +GoStart dt=5 g=14 g_seq=30 +GoLabel dt=1 label_string=2 +GoBlock dt=2423 reason_string=15 stack=5 +ProcStop dt=36 +ProcStart dt=7135 p=31 p_seq=2 +GoStart dt=228 g=127 g_seq=4 +GCMarkAssistEnd dt=9 +HeapAlloc dt=2440 heapalloc_value=192666328 +GoStop dt=28 reason_string=16 stack=4 +GoUnblock dt=19 g=52 g_seq=57 stack=0 +GoStart dt=6 g=52 g_seq=58 +GoLabel dt=1 label_string=2 +GoBlock dt=1072 reason_string=15 stack=5 +GoUnblock dt=16 g=52 g_seq=59 stack=0 +GoStart dt=6 g=52 g_seq=60 +GoLabel dt=1 label_string=2 +GoBlock dt=19 reason_string=15 stack=5 +GoUnblock dt=17 g=54 g_seq=39 stack=0 +GoStart dt=4 g=54 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=2352 reason_string=15 stack=5 +GoStart dt=22 g=127 g_seq=8 +GCMarkAssistBegin dt=127 stack=3 +GoBlock dt=42 reason_string=13 stack=11 +GoStart dt=766 g=122 g_seq=2 +GCMarkAssistEnd dt=2 +HeapAlloc dt=19 heapalloc_value=194902744 +GCMarkAssistBegin dt=66 stack=3 +STWBegin dt=12586 kind_string=21 stack=21 +GoUnblock dt=699 g=91 g_seq=3 stack=22 +GoUnblock dt=5 g=127 g_seq=9 stack=22 +GoUnblock dt=3 g=112 g_seq=8 stack=22 +GoUnblock dt=4 g=82 g_seq=10 stack=22 +GoUnblock dt=3 g=116 g_seq=11 stack=22 +GoUnblock dt=3 g=93 g_seq=8 stack=22 +GoUnblock dt=4 g=109 g_seq=6 stack=22 +GoUnblock dt=5 g=115 g_seq=9 stack=22 +GoUnblock dt=7 g=120 g_seq=7 stack=22 +GoUnblock dt=7 g=105 g_seq=15 stack=22 +GoUnblock dt=6 g=96 g_seq=7 stack=22 +GoUnblock dt=3 g=118 g_seq=6 stack=22 +GoUnblock dt=4 g=87 g_seq=7 stack=22 +GoUnblock dt=4 g=84 g_seq=9 stack=22 +GoUnblock dt=6 g=100 g_seq=6 stack=22 +GoUnblock dt=29 g=86 g_seq=6 stack=23 +HeapAlloc dt=53 heapalloc_value=103773088 +GoStatus dt=10 g=3 m=18446744073709551615 gstatus=4 +GoUnblock dt=7 g=3 g_seq=1 stack=24 +GCEnd dt=3 gc_seq=5 +HeapGoal dt=6 heapgoal_value=207987496 +ProcsChange dt=45 procs_value=48 stack=25 +STWEnd dt=399 +GoUnblock dt=5992 g=130 g_seq=6 stack=26 +GCMarkAssistEnd dt=9 +HeapAlloc dt=11 heapalloc_value=103816864 +GCSweepBegin dt=79 stack=27 +GCSweepEnd dt=1631 swept_value=8388608 reclaimed_value=3260416 +HeapAlloc dt=14 heapalloc_value=104810272 +HeapAlloc dt=104 heapalloc_value=105001504 +HeapAlloc dt=107 heapalloc_value=105164960 +HeapAlloc dt=55 heapalloc_value=105308320 +HeapAlloc dt=200 heapalloc_value=105798560 +HeapAlloc dt=119 heapalloc_value=106091424 +HeapAlloc dt=118 heapalloc_value=106359712 +HeapAlloc dt=47 heapalloc_value=106488096 +HeapAlloc dt=44 heapalloc_value=106763424 +HeapAlloc dt=26 heapalloc_value=106820768 +HeapAlloc dt=106 heapalloc_value=107277344 +HeapAlloc dt=131 heapalloc_value=107656992 +HeapAlloc dt=71 heapalloc_value=107790880 +GCSweepBegin dt=42 stack=28 +GCSweepEnd dt=6 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=3 heapalloc_value=107860512 +HeapAlloc dt=71 heapalloc_value=108305696 +HeapAlloc dt=113 heapalloc_value=108608928 +HeapAlloc dt=129 heapalloc_value=108890272 +HeapAlloc dt=147 heapalloc_value=109508896 +HeapAlloc dt=88 heapalloc_value=109776544 +HeapAlloc dt=140 heapalloc_value=110286976 +HeapAlloc dt=151 heapalloc_value=110900096 +HeapAlloc dt=152 heapalloc_value=111433600 +HeapAlloc dt=136 heapalloc_value=111931264 +HeapAlloc dt=67 heapalloc_value=112248064 +HeapAlloc dt=209 heapalloc_value=113046144 +HeapAlloc dt=213 heapalloc_value=113949056 +HeapAlloc dt=236 heapalloc_value=114471168 +HeapAlloc dt=90 heapalloc_value=114663552 +GCSweepBegin dt=45 stack=28 +GCSweepEnd dt=10 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=3 heapalloc_value=114703232 +GCSweepBegin dt=54 stack=27 +EventBatch gen=3 m=169396 time=28114950894859 size=148 +ProcStatus dt=2 p=1 pstatus=1 +GoStatus dt=4 g=86 m=169396 gstatus=2 +GCMarkAssistActive dt=2 g=86 +GCMarkAssistEnd dt=2 +HeapAlloc dt=42 heapalloc_value=189889984 +GoStop dt=32 reason_string=16 stack=4 +GoUnblock dt=117 g=69 g_seq=1 stack=0 +GoStart dt=6 g=69 g_seq=2 +GoLabel dt=1 label_string=2 +GoBlock dt=2672 reason_string=15 stack=5 +GoStart dt=16 g=84 g_seq=1 +GoStop dt=2565 reason_string=20 stack=9 +GoStart dt=17 g=84 g_seq=2 +GoBlock dt=886 reason_string=13 stack=11 +ProcStop dt=2581 +ProcStart dt=17 p=1 p_seq=1 +ProcStop dt=4400 +ProcStart dt=16 p=1 p_seq=2 +GoStart dt=24 g=87 g_seq=3 +GCMarkAssistEnd dt=8 +HeapAlloc dt=70 heapalloc_value=191782336 +GCMarkAssistBegin dt=85 stack=3 +GoBlock dt=1055 reason_string=13 stack=11 +GoUnblock dt=20 g=54 g_seq=9 stack=0 +GoStart dt=7 g=54 g_seq=10 +GoLabel dt=3 label_string=2 +GoBlock dt=230 reason_string=15 stack=5 +GoUnblock dt=12 g=54 g_seq=11 stack=0 +GoStart dt=6 g=54 g_seq=12 +GoLabel dt=1 label_string=2 +GoBlock dt=1754 reason_string=15 stack=5 +GoUnblock dt=12 g=54 g_seq=13 stack=0 +GoStart dt=8 g=54 g_seq=14 +GoLabel dt=3 label_string=2 +GoBlock dt=1379 reason_string=15 stack=5 +ProcStop dt=15 +EventBatch gen=3 m=169395 time=28114950898507 size=532 +ProcStatus dt=2 p=14 pstatus=1 +GoStatus dt=2 g=103 m=169395 gstatus=2 +GCMarkAssistActive dt=1 g=103 +GCMarkAssistEnd dt=3 +HeapAlloc dt=40 heapalloc_value=190873024 +HeapAlloc dt=75 heapalloc_value=191036864 +GCMarkAssistBegin dt=65 stack=3 +GoBlock dt=6142 reason_string=13 stack=11 +GoStart dt=19 g=98 g_seq=3 +GCMarkAssistBegin dt=20 stack=3 +GoStop dt=1738 reason_string=20 stack=9 +GoStart dt=16 g=98 g_seq=4 +GoBlock dt=2102 reason_string=13 stack=11 +GoUnblock dt=2317 g=71 g_seq=5 stack=0 +GoStart dt=5 g=71 g_seq=6 +GoLabel dt=2 label_string=4 +GoBlock dt=128 reason_string=15 stack=5 +GoUnblock dt=2283 g=71 g_seq=13 stack=0 +GoStart dt=7 g=71 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=97 reason_string=15 stack=5 +GoUnblock dt=1168 g=24 g_seq=13 stack=0 +GoStart dt=7 g=24 g_seq=14 +GoLabel dt=1 label_string=4 +GoBlock dt=1399 reason_string=15 stack=5 +GoUnblock dt=3752 g=23 g_seq=25 stack=0 +GoStart dt=6 g=23 g_seq=26 +GoLabel dt=3 label_string=4 +GoBlock dt=1167 reason_string=15 stack=5 +GoUnblock dt=99 g=52 g_seq=23 stack=0 +GoStart dt=35 g=52 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=47 reason_string=15 stack=5 +GoUnblock dt=81 g=67 g_seq=19 stack=0 +GoStart dt=8 g=67 g_seq=20 +GoLabel dt=3 label_string=4 +GoBlock dt=3975 reason_string=15 stack=5 +GoUnblock dt=18 g=67 g_seq=21 stack=0 +GoStart dt=6 g=67 g_seq=22 +GoLabel dt=1 label_string=2 +GoBlock dt=80 reason_string=15 stack=5 +GoUnblock dt=18 g=67 g_seq=23 stack=0 +GoStart dt=6 g=67 g_seq=24 +GoLabel dt=1 label_string=2 +GoBlock dt=22 reason_string=15 stack=5 +GoUnblock dt=3174 g=14 g_seq=23 stack=0 +GoStart dt=7 g=14 g_seq=24 +GoLabel dt=1 label_string=4 +GoBlock dt=22 reason_string=15 stack=5 +GoUnblock dt=9 g=14 g_seq=25 stack=0 +GoStart dt=2 g=14 g_seq=26 +GoLabel dt=1 label_string=2 +GoBlock dt=13 reason_string=15 stack=5 +GoUnblock dt=65 g=29 g_seq=29 stack=0 +GoStart dt=8 g=29 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=18 reason_string=15 stack=5 +GoUnblock dt=13 g=29 g_seq=31 stack=0 +GoStart dt=6 g=29 g_seq=32 +GoLabel dt=2 label_string=2 +GoBlock dt=21 reason_string=15 stack=5 +GoUnblock dt=19 g=24 g_seq=37 stack=0 +GoStart dt=4 g=24 g_seq=38 +GoLabel dt=2 label_string=2 +GoBlock dt=33 reason_string=15 stack=5 +GoUnblock dt=8 g=24 g_seq=39 stack=0 +GoStart dt=3 g=24 g_seq=40 +GoLabel dt=1 label_string=2 +GoBlock dt=32 reason_string=15 stack=5 +GoUnblock dt=80 g=25 g_seq=29 stack=0 +GoStart dt=9 g=25 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=20 reason_string=15 stack=5 +GoUnblock dt=27 g=24 g_seq=43 stack=0 +GoStart dt=6 g=24 g_seq=44 +GoLabel dt=1 label_string=2 +GoBlock dt=185 reason_string=15 stack=5 +GoUnblock dt=9 g=24 g_seq=45 stack=0 +GoStart dt=6 g=24 g_seq=46 +GoLabel dt=3 label_string=2 +GoBlock dt=10 reason_string=15 stack=5 +GoUnblock dt=6 g=24 g_seq=47 stack=0 +GoStart dt=1 g=24 g_seq=48 +GoLabel dt=1 label_string=2 +GoBlock dt=41 reason_string=15 stack=5 +ProcStop dt=59 +ProcStart dt=21430 p=4 p_seq=1 +GoStart dt=238 g=102 g_seq=4 +GCMarkAssistEnd dt=10 +HeapAlloc dt=38 heapalloc_value=196352464 +GoStop dt=5526 reason_string=16 stack=6 +ProcStop dt=240 +ProcStart dt=11401 p=6 p_seq=1 +GoStart dt=196 g=109 g_seq=7 +GCMarkAssistEnd dt=5 +HeapAlloc dt=54 heapalloc_value=108264736 +HeapAlloc dt=117 heapalloc_value=108527008 +HeapAlloc dt=77 heapalloc_value=108783776 +HeapAlloc dt=90 heapalloc_value=109036320 +HeapAlloc dt=77 heapalloc_value=109355808 +HeapAlloc dt=106 heapalloc_value=109678240 +HeapAlloc dt=70 heapalloc_value=110030624 +HeapAlloc dt=90 heapalloc_value=110205056 +HeapAlloc dt=51 heapalloc_value=110347136 +HeapAlloc dt=63 heapalloc_value=110588800 +HeapAlloc dt=69 heapalloc_value=110912384 +HeapAlloc dt=42 heapalloc_value=111111808 +HeapAlloc dt=105 heapalloc_value=111452032 +HeapAlloc dt=89 heapalloc_value=111822720 +HeapAlloc dt=106 heapalloc_value=112260352 +HeapAlloc dt=55 heapalloc_value=112397056 +HeapAlloc dt=62 heapalloc_value=112682368 +HeapAlloc dt=137 heapalloc_value=113281920 +GCSweepBegin dt=50 stack=28 +GCSweepEnd dt=8 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=4 heapalloc_value=113424000 +HeapAlloc dt=92 heapalloc_value=113908096 +GCSweepBegin dt=145 stack=31 +EventBatch gen=3 m=169394 time=28114950898962 size=373 +ProcStatus dt=1 p=20 pstatus=1 +GoStatus dt=4 g=108 m=169394 gstatus=2 +GCMarkAssistActive dt=1 g=108 +GCMarkAssistEnd dt=2 +HeapAlloc dt=25 heapalloc_value=191102400 +GCMarkAssistBegin dt=104 stack=3 +GCMarkAssistEnd dt=2445 +HeapAlloc dt=47 heapalloc_value=191372736 +GCMarkAssistBegin dt=11 stack=3 +GoBlock dt=1789 reason_string=13 stack=11 +GoUnblock dt=19 g=22 g_seq=3 stack=0 +GoStart dt=7 g=22 g_seq=4 +GoLabel dt=1 label_string=2 +GoBlock dt=3342 reason_string=15 stack=5 +GoUnblock dt=2752 g=71 g_seq=1 stack=0 +GoStart dt=7 g=71 g_seq=2 +GoLabel dt=1 label_string=4 +GoBlock dt=269 reason_string=15 stack=5 +GoStart dt=4308 g=111 g_seq=3 +GCMarkAssistEnd dt=7 +HeapAlloc dt=58 heapalloc_value=191888832 +GCMarkAssistBegin dt=42 stack=3 +GoBlock dt=148 reason_string=13 stack=11 +GoUnblock dt=1120 g=72 g_seq=25 stack=0 +GoStart dt=5 g=72 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=640 reason_string=15 stack=5 +GoStart dt=1105 g=102 g_seq=2 +GoStatus dt=19 g=117 m=18446744073709551615 gstatus=4 +GoUnblock dt=4 g=117 g_seq=1 stack=10 +GCMarkAssistBegin dt=13 stack=3 +GoBlock dt=32 reason_string=13 stack=11 +GoStart dt=8 g=117 g_seq=2 +GoStatus dt=19 g=128 m=18446744073709551615 gstatus=4 +GoUnblock dt=2 g=128 g_seq=1 stack=10 +GCMarkAssistBegin dt=5 stack=3 +GoBlock dt=15 reason_string=13 stack=11 +GoStart dt=5 g=128 g_seq=2 +GoStatus dt=12 g=92 m=18446744073709551615 gstatus=4 +GoUnblock dt=1 g=92 g_seq=1 stack=10 +GCMarkAssistBegin dt=9 stack=3 +GoBlock dt=14 reason_string=13 stack=11 +GoStart dt=7 g=92 g_seq=2 +GoStatus dt=17 g=101 m=18446744073709551615 gstatus=4 +GoUnblock dt=1 g=101 g_seq=1 stack=10 +GCMarkAssistBegin dt=7 stack=3 +GoBlock dt=10 reason_string=13 stack=11 +GoStart dt=5 g=101 g_seq=2 +GoStatus dt=11 g=99 m=18446744073709551615 gstatus=4 +GoUnblock dt=1 g=99 g_seq=1 stack=10 +GCMarkAssistBegin dt=8 stack=3 +GoBlock dt=15 reason_string=13 stack=11 +GoStart dt=6 g=99 g_seq=2 +GoStatus dt=11 g=89 m=18446744073709551615 gstatus=4 +GoUnblock dt=1 g=89 g_seq=1 stack=10 +GCMarkAssistBegin dt=10 stack=3 +GoBlock dt=15 reason_string=13 stack=11 +GoStart dt=4 g=89 g_seq=2 +GoStatus dt=11 g=124 m=18446744073709551615 gstatus=4 +GoUnblock dt=2 g=124 g_seq=1 stack=10 +GCMarkAssistBegin dt=8 stack=3 +GoBlock dt=34 reason_string=13 stack=11 +GoStart dt=5 g=124 g_seq=2 +GoStatus dt=10 g=96 m=18446744073709551615 gstatus=4 +GoUnblock dt=1 g=96 g_seq=1 stack=10 +GCMarkAssistBegin dt=4 stack=3 +GoBlock dt=14 reason_string=13 stack=11 +GoStart dt=4 g=96 g_seq=2 +GCMarkAssistBegin dt=8 stack=3 +GoBlock dt=22 reason_string=13 stack=11 +ProcStop dt=16 +EventBatch gen=3 m=169393 time=28114950894837 size=271 +ProcStatus dt=2 p=16 pstatus=1 +GoStatus dt=2 g=69 m=169393 gstatus=2 +GoBlock dt=122 reason_string=15 stack=5 +GoStatus dt=2224 g=83 m=169393 gstatus=1 +GoStart dt=1 g=83 g_seq=1 +GoStatus dt=33 g=121 m=18446744073709551615 gstatus=4 +GoUnblock dt=10 g=121 g_seq=1 stack=10 +GCMarkAssistBegin dt=16 stack=3 +GoStop dt=620 reason_string=20 stack=9 +GoStart dt=11 g=121 g_seq=2 +GoStatus dt=18 g=110 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=110 g_seq=1 stack=10 +GCMarkAssistBegin dt=12 stack=3 +GoStop dt=1840 reason_string=20 stack=9 +GoStart dt=16 g=110 g_seq=2 +GoStatus dt=19 g=125 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=125 g_seq=1 stack=10 +GCMarkAssistBegin dt=10 stack=3 +GoBlock dt=1799 reason_string=13 stack=11 +GoStart dt=1317 g=127 g_seq=2 +GoStatus dt=21 g=116 m=18446744073709551615 gstatus=4 +GoUnblock dt=9 g=116 g_seq=1 stack=10 +GCMarkAssistBegin dt=16 stack=3 +GoBlock dt=473 reason_string=13 stack=11 +GoStart dt=28 g=116 g_seq=2 +GoStatus dt=14 g=119 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=119 g_seq=1 stack=10 +GCMarkAssistBegin dt=12 stack=3 +GoStop dt=570 reason_string=20 stack=9 +GoStart dt=24 g=119 g_seq=2 +GoStatus dt=18 g=95 m=18446744073709551615 gstatus=4 +GoUnblock dt=3 g=95 g_seq=1 stack=10 +GCMarkAssistBegin dt=11 stack=3 +GoBlock dt=5206 reason_string=13 stack=11 +ProcStop dt=2547 +ProcStart dt=26 p=16 p_seq=1 +GoUnblock dt=87 g=58 g_seq=15 stack=0 +GoStart dt=8 g=58 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=579 reason_string=15 stack=5 +GoUnblock dt=23 g=69 g_seq=15 stack=0 +GoStart dt=5 g=69 g_seq=16 +GoLabel dt=1 label_string=2 +GoBlock dt=1028 reason_string=15 stack=5 +GoUnblock dt=2356 g=14 g_seq=11 stack=0 +GoStart dt=6 g=14 g_seq=12 +GoLabel dt=1 label_string=4 +GoBlock dt=1282 reason_string=15 stack=5 +ProcStop dt=8 +EventBatch gen=3 m=169392 time=28114950898262 size=651 +ProcStatus dt=1 p=3 pstatus=1 +GoStatus dt=1 g=106 m=169392 gstatus=2 +GCMarkAssistActive dt=1 g=106 +GCMarkAssistEnd dt=3 +HeapAlloc dt=34 heapalloc_value=190807488 +HeapAlloc dt=125 heapalloc_value=190832064 +GCMarkAssistBegin dt=46 stack=3 +GoBlock dt=1002 reason_string=13 stack=11 +GoStart dt=28 g=82 g_seq=2 +GoBlock dt=1446 reason_string=13 stack=11 +GoStart dt=34 g=120 g_seq=3 +GCMarkAssistEnd dt=2 +HeapAlloc dt=32 heapalloc_value=191282624 +GCMarkAssistBegin dt=115 stack=3 +GoBlock dt=25 reason_string=13 stack=11 +GoStart dt=17 g=112 g_seq=2 +GoBlock dt=2074 reason_string=13 stack=11 +GoUnblock dt=2604 g=24 g_seq=5 stack=0 +GoStart dt=7 g=24 g_seq=6 +GoLabel dt=2 label_string=4 +GoBlock dt=278 reason_string=15 stack=5 +GoUnblock dt=2267 g=58 g_seq=5 stack=0 +GoStart dt=9 g=58 g_seq=6 +GoLabel dt=1 label_string=4 +GoBlock dt=316 reason_string=15 stack=5 +GoUnblock dt=1167 g=24 g_seq=7 stack=0 +GoStart dt=6 g=24 g_seq=8 +GoLabel dt=1 label_string=4 +GoBlock dt=171 reason_string=15 stack=5 +GoUnblock dt=1155 g=71 g_seq=7 stack=0 +GoStart dt=6 g=71 g_seq=8 +GoLabel dt=1 label_string=4 +GoBlock dt=32 reason_string=15 stack=5 +GoStart dt=3316 g=109 g_seq=2 +GoStatus dt=28 g=114 m=18446744073709551615 gstatus=4 +GoUnblock dt=8 g=114 g_seq=1 stack=10 +GCMarkAssistBegin dt=18 stack=3 +GoStop dt=3860 reason_string=20 stack=9 +GoUnblock dt=14 g=57 g_seq=31 stack=0 +GoStart dt=5 g=57 g_seq=32 +GoLabel dt=3 label_string=2 +GoBlock dt=3324 reason_string=15 stack=5 +GoUnblock dt=97 g=24 g_seq=25 stack=0 +GoStart dt=6 g=24 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=1146 reason_string=15 stack=5 +GoUnblock dt=73 g=24 g_seq=27 stack=0 +GoStart dt=4 g=24 g_seq=28 +GoLabel dt=1 label_string=4 +GoUnblock dt=2655 g=81 g_seq=4 stack=12 +GoBlock dt=402 reason_string=15 stack=5 +GoUnblock dt=9 g=24 g_seq=29 stack=0 +GoStart dt=7 g=24 g_seq=30 +GoLabel dt=1 label_string=2 +GoBlock dt=492 reason_string=15 stack=5 +GoUnblock dt=21 g=69 g_seq=27 stack=0 +GoStart dt=6 g=69 g_seq=28 +GoLabel dt=1 label_string=2 +GoBlock dt=20 reason_string=15 stack=5 +GoUnblock dt=11 g=69 g_seq=29 stack=0 +GoStart dt=3 g=69 g_seq=30 +GoLabel dt=1 label_string=2 +GoBlock dt=459 reason_string=15 stack=5 +GoStart dt=168 g=116 g_seq=6 +GCMarkAssistEnd dt=8 +HeapAlloc dt=61 heapalloc_value=192232224 +GCMarkAssistBegin dt=39 stack=3 +GoBlock dt=2360 reason_string=13 stack=11 +ProcStop dt=53 +ProcStart dt=14760 p=10 p_seq=2 +GoStart dt=211 g=99 g_seq=5 +GCMarkAssistBegin dt=93 stack=3 +GoBlock dt=33 reason_string=13 stack=11 +GoStart dt=9 g=120 g_seq=6 +GCMarkAssistBegin dt=78 stack=3 +GoBlock dt=102 reason_string=13 stack=11 +GoStart dt=31 g=108 g_seq=2 +GCMarkAssistEnd dt=6 +HeapAlloc dt=307 heapalloc_value=194853592 +GoStop dt=7166 reason_string=16 stack=6 +GoStart dt=86 g=128 g_seq=6 +HeapAlloc dt=4873 heapalloc_value=196688336 +GoStop dt=12 reason_string=16 stack=6 +ProcStop dt=395 +ProcStart dt=8670 p=3 p_seq=2 +GoStart dt=193 g=93 g_seq=9 +GCMarkAssistEnd dt=7 +HeapAlloc dt=78 heapalloc_value=104465440 +HeapAlloc dt=122 heapalloc_value=104583584 +HeapAlloc dt=92 heapalloc_value=104769312 +HeapAlloc dt=127 heapalloc_value=104935968 +GCSweepBegin dt=109 stack=28 +GCSweepEnd dt=9 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=2 heapalloc_value=105138720 +HeapAlloc dt=77 heapalloc_value=105373856 +GCSweepBegin dt=157 stack=28 +GCSweepEnd dt=8 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=3 heapalloc_value=105708448 +GCSweepBegin dt=56 stack=28 +GCSweepEnd dt=11 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=4 heapalloc_value=105880480 +GCSweepBegin dt=48 stack=28 +GCSweepEnd dt=10 swept_value=32768 reclaimed_value=32768 +HeapAlloc dt=4 heapalloc_value=106124192 +GCSweepBegin dt=79 stack=28 +GCSweepEnd dt=7 swept_value=8192 reclaimed_value=8192 +HeapAlloc dt=2 heapalloc_value=106283168 +HeapAlloc dt=98 heapalloc_value=106567968 +HeapAlloc dt=116 heapalloc_value=107070496 +HeapAlloc dt=30 heapalloc_value=107146272 +HeapAlloc dt=105 heapalloc_value=107517728 +HeapAlloc dt=169 heapalloc_value=108084512 +HeapAlloc dt=187 heapalloc_value=108649888 +HeapAlloc dt=158 heapalloc_value=109200160 +HeapAlloc dt=200 heapalloc_value=109872160 +GCSweepBegin dt=116 stack=28 +GCSweepEnd dt=9 swept_value=24576 reclaimed_value=24576 +HeapAlloc dt=3 heapalloc_value=110229632 +HeapAlloc dt=54 heapalloc_value=110441344 +HeapAlloc dt=76 heapalloc_value=110711680 +HeapAlloc dt=100 heapalloc_value=111216768 +HeapAlloc dt=156 heapalloc_value=111708032 +HeapAlloc dt=55 heapalloc_value=111972224 +HeapAlloc dt=122 heapalloc_value=112391424 +HeapAlloc dt=160 heapalloc_value=113099392 +HeapAlloc dt=191 heapalloc_value=113713536 +HeapAlloc dt=158 heapalloc_value=114362368 +GCSweepBegin dt=88 stack=28 +GCSweepEnd dt=14 swept_value=16384 reclaimed_value=16384 +HeapAlloc dt=9 heapalloc_value=114520320 +HeapAlloc dt=56 heapalloc_value=114636672 +GCSweepBegin dt=180 stack=27 +EventBatch gen=3 m=169390 time=28114950895313 size=834 +ProcStatus dt=1 p=27 pstatus=1 +GoStatus dt=3 g=82 m=169390 gstatus=2 +GCMarkAssistActive dt=1 g=82 +GCMarkAssistEnd dt=2 +HeapAlloc dt=28 heapalloc_value=190143936 +HeapAlloc dt=270 heapalloc_value=190201280 +HeapAlloc dt=96 heapalloc_value=190209472 +HeapAlloc dt=29 heapalloc_value=190258624 +HeapAlloc dt=107 heapalloc_value=190356928 +GCMarkAssistBegin dt=57 stack=3 +GCMarkAssistEnd dt=502 +HeapAlloc dt=27 heapalloc_value=190430656 +GoStop dt=26 reason_string=16 stack=4 +GoStart dt=12 g=131 g_seq=3 +GoSyscallBegin dt=17 p_seq=1 stack=7 +GoSyscallEnd dt=205 +GoSyscallBegin dt=19 p_seq=2 stack=7 +GoSyscallEnd dt=2580 +GoSyscallBegin dt=16 p_seq=3 stack=7 +GoSyscallEnd dt=71 +GoSyscallBegin dt=15 p_seq=4 stack=7 +GoSyscallEnd dt=72 +GoSyscallBegin dt=25 p_seq=5 stack=7 +GoSyscallEnd dt=76 +GoSyscallBegin dt=12 p_seq=6 stack=7 +GoSyscallEnd dt=69 +GoSyscallBegin dt=11 p_seq=7 stack=7 +GoSyscallEnd dt=62 +GoSyscallBegin dt=13 p_seq=8 stack=7 +GoSyscallEnd dt=67 +GoSyscallBegin dt=16 p_seq=9 stack=7 +GoSyscallEnd dt=64 +GoSyscallBegin dt=12 p_seq=10 stack=7 +GoSyscallEnd dt=65 +GoSyscallBegin dt=14 p_seq=11 stack=7 +GoSyscallEnd dt=226 +GoSyscallBegin dt=14 p_seq=12 stack=7 +GoSyscallEnd dt=69 +GoSyscallBegin dt=17 p_seq=13 stack=7 +GoSyscallEnd dt=72 +GoSyscallBegin dt=15 p_seq=14 stack=7 +GoSyscallEnd dt=66 +GoSyscallBegin dt=18 p_seq=15 stack=7 +GoSyscallEnd dt=63 +GoSyscallBegin dt=13 p_seq=16 stack=7 +GoSyscallEnd dt=69 +GoSyscallBegin dt=17 p_seq=17 stack=7 +GoSyscallEnd dt=66 +GoSyscallBegin dt=109 p_seq=18 stack=7 +GoSyscallEnd dt=73 +GoSyscallBegin dt=13 p_seq=19 stack=7 +GoSyscallEnd dt=68 +GoSyscallBegin dt=16 p_seq=20 stack=7 +GoSyscallEnd dt=63 +GoSyscallBegin dt=15 p_seq=21 stack=7 +GoSyscallEnd dt=82 +GoSyscallBegin dt=11 p_seq=22 stack=7 +GoSyscallEnd dt=177 +GoSyscallBegin dt=14 p_seq=23 stack=7 +GoSyscallEnd dt=62 +GoSyscallBegin dt=13 p_seq=24 stack=7 +GoSyscallEnd dt=90 +GoSyscallBegin dt=11 p_seq=25 stack=7 +GoSyscallEnd dt=69 +GoSyscallBegin dt=13 p_seq=26 stack=7 +GoSyscallEnd dt=65 +GoSyscallBegin dt=15 p_seq=27 stack=7 +GoSyscallEnd dt=72 +GoSyscallBegin dt=15 p_seq=28 stack=7 +GoSyscallEnd dt=73 +GoSyscallBegin dt=18 p_seq=29 stack=7 +GoSyscallEnd dt=80 +GoSyscallBegin dt=21 p_seq=30 stack=7 +GoSyscallEnd dt=72 +GoSyscallBegin dt=17 p_seq=31 stack=7 +GoSyscallEnd dt=67 +GoSyscallBegin dt=12 p_seq=32 stack=7 +GoSyscallEnd dt=171 +GoSyscallBegin dt=16 p_seq=33 stack=7 +GoSyscallEnd dt=76 +GoSyscallBegin dt=18 p_seq=34 stack=7 +GoSyscallEnd dt=78 +GoSyscallBegin dt=13 p_seq=35 stack=7 +GoSyscallEnd dt=77 +GoSyscallBegin dt=20 p_seq=36 stack=7 +GoSyscallEnd dt=77 +GoBlock dt=16 reason_string=15 stack=2 +GoUnblock dt=1400 g=54 g_seq=3 stack=0 +GoStart dt=8 g=54 g_seq=4 +GoLabel dt=1 label_string=4 +GoBlock dt=2659 reason_string=15 stack=5 +GoUnblock dt=13 g=22 g_seq=5 stack=0 +GoStart dt=5 g=22 g_seq=6 +GoLabel dt=1 label_string=2 +GoBlock dt=2498 reason_string=15 stack=5 +GoUnblock dt=10 g=22 g_seq=7 stack=0 +GoStart dt=7 g=22 g_seq=8 +GoLabel dt=2 label_string=2 +GoBlock dt=4213 reason_string=15 stack=5 +GoUnblock dt=1324 g=57 g_seq=25 stack=0 +GoStart dt=11 g=57 g_seq=26 +GoLabel dt=1 label_string=4 +GoBlock dt=256 reason_string=15 stack=5 +GoUnblock dt=8 g=57 g_seq=27 stack=0 +GoStart dt=5 g=57 g_seq=28 +GoLabel dt=1 label_string=2 +GoBlock dt=485 reason_string=15 stack=5 +GoUnblock dt=8 g=57 g_seq=29 stack=0 +GoStart dt=6 g=57 g_seq=30 +GoLabel dt=3 label_string=2 +GoBlock dt=504 reason_string=15 stack=5 +ProcStop dt=3771 +ProcStart dt=29 p=27 p_seq=37 +GoUnblock dt=9 g=22 g_seq=15 stack=0 +GoStart dt=5 g=22 g_seq=16 +GoLabel dt=1 label_string=4 +GoBlock dt=123 reason_string=15 stack=5 +GoUnblock dt=19 g=28 g_seq=7 stack=0 +GoStart dt=2 g=28 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=67 reason_string=15 stack=5 +GoUnblock dt=73 g=72 g_seq=29 stack=0 +GoStart dt=8 g=72 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=1357 reason_string=15 stack=5 +GoUnblock dt=71 g=53 g_seq=15 stack=0 +GoStart dt=5 g=53 g_seq=16 +GoLabel dt=2 label_string=4 +GoBlock dt=53 reason_string=15 stack=5 +ProcStop dt=61 +ProcStart dt=29 p=27 p_seq=38 +GoUnblock dt=4 g=72 g_seq=35 stack=0 +GoStart dt=4 g=72 g_seq=36 +GoLabel dt=1 label_string=4 +GoBlock dt=775 reason_string=15 stack=5 +GoUnblock dt=11 g=72 g_seq=37 stack=0 +GoStart dt=5 g=72 g_seq=38 +GoLabel dt=3 label_string=2 +GoBlock dt=2553 reason_string=15 stack=5 +GoUnblock dt=23 g=54 g_seq=27 stack=0 +GoStart dt=7 g=54 g_seq=28 +GoLabel dt=1 label_string=2 +GoBlock dt=5185 reason_string=15 stack=5 +ProcStop dt=46 +ProcStart dt=1102 p=27 p_seq=39 +GoUnblock dt=17 g=14 g_seq=31 stack=0 +GoStart dt=191 g=14 g_seq=32 +GoLabel dt=5 label_string=2 +GoBlock dt=26 reason_string=15 stack=5 +GoUnblock dt=7 g=14 g_seq=33 stack=0 +GoStart dt=2 g=14 g_seq=34 +GoLabel dt=1 label_string=2 +GoBlock dt=81 reason_string=15 stack=5 +GoUnblock dt=11 g=14 g_seq=35 stack=0 +GoStart dt=6 g=14 g_seq=36 +GoLabel dt=1 label_string=2 +GoUnblock dt=257 g=97 g_seq=3 stack=12 +GoStop dt=1123 reason_string=16 stack=13 +GoUnblock dt=612 g=131 g_seq=4 stack=0 +GoStart dt=5 g=131 g_seq=5 +GoSyscallBegin dt=23 p_seq=40 stack=7 +GoSyscallEnd dt=200 +GoSyscallBegin dt=13 p_seq=41 stack=7 +GoSyscallEnd dt=179 +GoBlock dt=6 reason_string=15 stack=2 +ProcStop dt=31 +ProcStart dt=1232 p=22 p_seq=3 +GoUnblock dt=16 g=14 g_seq=40 stack=0 +GoStart dt=157 g=14 g_seq=41 +GoLabel dt=2 label_string=2 +GoUnblock dt=343 g=103 g_seq=1 stack=12 +GoBlock dt=2805 reason_string=15 stack=5 +ProcStop dt=68 +ProcStart dt=17 p=22 p_seq=4 +GoUnblock dt=3 g=14 g_seq=42 stack=0 +GoStart dt=4 g=14 g_seq=43 +GoLabel dt=1 label_string=4 +GoUnblock dt=609 g=116 g_seq=7 stack=12 +GoBlock dt=9 reason_string=15 stack=5 +GoStart dt=10 g=116 g_seq=8 +GCMarkAssistEnd dt=7 +HeapAlloc dt=60 heapalloc_value=192527064 +GCMarkAssistBegin dt=41 stack=3 +GoBlock dt=47 reason_string=13 stack=11 +GoUnblock dt=13 g=30 g_seq=35 stack=0 +GoStart dt=4 g=30 g_seq=36 +GoLabel dt=2 label_string=2 +GoBlock dt=266 reason_string=15 stack=5 +GoStart dt=16 g=105 g_seq=8 +GoBlock dt=18 reason_string=13 stack=11 +GoUnblock dt=55 g=54 g_seq=29 stack=0 +GoStart dt=8 g=54 g_seq=30 +GoLabel dt=1 label_string=4 +GoBlock dt=13 reason_string=15 stack=5 +GoUnblock dt=10 g=54 g_seq=31 stack=0 +GoStart dt=1 g=54 g_seq=32 +GoLabel dt=1 label_string=2 +GoBlock dt=46 reason_string=15 stack=5 +ProcStop dt=57 +ProcStart dt=14 p=22 p_seq=5 +GoUnblock dt=4 g=54 g_seq=33 stack=0 +GoStart dt=159 g=54 g_seq=34 +GoLabel dt=1 label_string=4 +GoBlock dt=8 reason_string=15 stack=5 +ProcStop dt=32 +ProcStart dt=3156 p=29 p_seq=1 +GoUnblock dt=15 g=71 g_seq=43 stack=0 +GoStart dt=165 g=71 g_seq=44 +GoLabel dt=1 label_string=2 +GoBlock dt=1463 reason_string=15 stack=5 +GoStart dt=22 g=118 g_seq=4 +GCMarkAssistEnd dt=6 +HeapAlloc dt=903 heapalloc_value=195328728 +GoStop dt=6525 reason_string=16 stack=6 +GoStart dt=46 g=118 g_seq=5 +GCMarkAssistBegin dt=12 stack=3 +GoBlock dt=31 reason_string=13 stack=11 +ProcStop dt=194 +EventBatch gen=3 m=18446744073709551615 time=28114950975784 size=435 +GoStatus dt=1 g=1 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=2 m=18446744073709551615 gstatus=4 +GoStatus dt=6 g=4 m=18446744073709551615 gstatus=4 +GoStatus dt=5 g=5 m=18446744073709551615 gstatus=4 +GoStatus dt=4 g=6 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=7 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=17 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=33 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=8 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=9 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=10 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=18 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=11 m=18446744073709551615 gstatus=4 +GoStatus dt=4 g=34 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=19 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=12 m=18446744073709551615 gstatus=4 +GoStatus dt=2 g=20 m=18446744073709551615 gstatus=4 +GoStatus dt=4 g=35 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=13 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=21 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=36 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=49 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=50 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=15 m=18446744073709551615 gstatus=4 +GoStatus dt=4 g=65 m=18446744073709551615 gstatus=4 +GoStatus dt=2 g=66 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=26 m=18446744073709551615 gstatus=4 +GoStatus dt=4 g=55 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=27 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=37 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=129 m=18446744073709551615 gstatus=4 +EventBatch gen=3 m=18446744073709551615 time=28114950976078 size=1132 +Stacks +Stack id=20 nframes=2 + pc=4540421 func=22 file=23 line=363 + pc=4546157 func=24 file=23 line=874 +Stack id=21 nframes=5 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=18 nframes=6 + pc=4296626 func=34 file=35 line=807 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=26 nframes=7 + pc=4300939 func=36 file=35 line=1196 + pc=4297301 func=34 file=35 line=926 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=7 nframes=7 + pc=4709082 func=37 file=38 line=964 + pc=4738119 func=39 file=40 line=209 + pc=4738111 func=41 file=42 line=736 + pc=4737664 func=43 file=42 line=380 + pc=4739536 func=44 file=45 line=46 + pc=4739528 func=46 file=47 line=183 + pc=4803162 func=48 file=49 line=134 +Stack id=10 nframes=4 + pc=4295522 func=50 file=35 line=627 + pc=4246870 func=29 file=28 line=1288 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=29 nframes=8 + pc=4556437 func=51 file=52 line=352 + pc=4341796 func=53 file=54 line=521 + pc=4279859 func=55 file=56 line=127 + pc=4277746 func=57 file=58 line=182 + pc=4244580 func=59 file=28 line=944 + pc=4245653 func=29 file=28 line=1116 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=14 nframes=1 + pc=4546157 func=24 file=23 line=874 +Stack id=17 nframes=1 + pc=0 func=0 file=0 line=0 +Stack id=19 nframes=2 + pc=4540420 func=22 file=23 line=353 + pc=4546157 func=24 file=23 line=874 +Stack id=13 nframes=1 + pc=0 func=0 file=0 line=0 +Stack id=5 nframes=2 + pc=4418893 func=60 file=61 line=402 + pc=4301860 func=62 file=35 line=1297 +Stack id=25 nframes=7 + pc=4298957 func=36 file=35 line=1087 + pc=4297301 func=34 file=35 line=926 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=4 nframes=2 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=30 nframes=6 + pc=4297308 func=34 file=35 line=817 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=11 nframes=6 + pc=4314276 func=63 file=26 line=749 + pc=4312530 func=25 file=26 line=589 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=6 nframes=2 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=15 nframes=1 + pc=4546157 func=24 file=23 line=874 +Stack id=8 nframes=1 + pc=0 func=0 file=0 line=0 +Stack id=12 nframes=2 + pc=4614055 func=64 file=65 line=474 + pc=4302129 func=62 file=35 line=1357 +Stack id=3 nframes=6 + pc=4556897 func=66 file=52 line=378 + pc=4312252 func=25 file=26 line=536 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=9 nframes=5 + pc=4312495 func=25 file=26 line=576 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=24 nframes=8 + pc=4614055 func=64 file=65 line=474 + pc=4298031 func=36 file=35 line=964 + pc=4297301 func=34 file=35 line=926 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=23 nframes=6 + pc=4297239 func=34 file=35 line=914 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=2 nframes=1 + pc=4803172 func=48 file=49 line=130 +Stack id=28 nframes=8 + pc=4556437 func=51 file=52 line=352 + pc=4341796 func=53 file=54 line=521 + pc=4280028 func=55 file=56 line=147 + pc=4277746 func=57 file=58 line=182 + pc=4244580 func=59 file=28 line=944 + pc=4246070 func=29 file=28 line=1145 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=27 nframes=5 + pc=4353658 func=67 file=68 line=958 + pc=4278148 func=69 file=58 line=234 + pc=4246244 func=29 file=28 line=1160 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=16 nframes=3 + pc=4217457 func=70 file=71 line=442 + pc=4546317 func=72 file=23 line=918 + pc=4546150 func=24 file=23 line=871 +Stack id=31 nframes=8 + pc=4353658 func=67 file=68 line=958 + pc=4280657 func=73 file=56 line=254 + pc=4280247 func=55 file=56 line=170 + pc=4277746 func=57 file=58 line=182 + pc=4244580 func=59 file=28 line=944 + pc=4246070 func=29 file=28 line=1145 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +Stack id=1 nframes=3 + pc=4554859 func=74 file=52 line=255 + pc=4540633 func=22 file=23 line=391 + pc=4546157 func=24 file=23 line=874 +Stack id=22 nframes=10 + pc=4558967 func=75 file=76 line=166 + pc=4558898 func=77 file=52 line=445 + pc=4447453 func=78 file=61 line=3712 + pc=4314041 func=79 file=26 line=714 + pc=4297238 func=34 file=35 line=909 + pc=4312466 func=25 file=26 line=564 + pc=4247187 func=27 file=28 line=1333 + pc=4245160 func=29 file=28 line=1021 + pc=4502184 func=30 file=31 line=103 + pc=4804475 func=32 file=33 line=60 +EventBatch gen=3 m=18446744073709551615 time=28114950894688 size=2762 +Strings +String id=1 + data="Not worker" +String id=2 + data="GC (dedicated)" +String id=3 + data="GC (fractional)" +String id=4 + data="GC (idle)" +String id=5 + data="unspecified" +String id=6 + data="forever" +String id=7 + data="network" +String id=8 + data="select" +String id=9 + data="sync.(*Cond).Wait" +String id=10 + data="sync" +String id=11 + data="chan send" +String id=12 + data="chan receive" +String id=13 + data="GC mark assist wait for work" +String id=14 + data="GC background sweeper wait" +String id=15 + data="system goroutine wait" +String id=16 + data="preempted" +String id=17 + data="wait for debug call" +String id=18 + data="wait until GC ends" +String id=19 + data="sleep" +String id=20 + data="runtime.Gosched" +String id=21 + data="GC mark termination" +String id=22 + data="runtime.traceAdvance" +String id=23 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" +String id=24 + data="runtime.(*traceAdvancerState).start.func1" +String id=25 + data="runtime.gcAssistAlloc" +String id=26 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgcmark.go" +String id=27 + data="runtime.deductAssistCredit" +String id=28 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/malloc.go" +String id=29 + data="runtime.mallocgc" +String id=30 + data="runtime.makeslice" +String id=31 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/slice.go" +String id=32 + data="main.main.func1" +String id=33 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/trace/v2/testdata/testprog/gc-stress.go" +String id=34 + data="runtime.gcMarkDone" +String id=35 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgc.go" +String id=36 + data="runtime.gcMarkTermination" +String id=37 + data="syscall.write" +String id=38 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/zsyscall_linux_amd64.go" +String id=39 + data="syscall.Write" +String id=40 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/syscall_unix.go" +String id=41 + data="internal/poll.ignoringEINTRIO" +String id=42 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_unix.go" +String id=43 + data="internal/poll.(*FD).Write" +String id=44 + data="os.(*File).write" +String id=45 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_posix.go" +String id=46 + data="os.(*File).Write" +String id=47 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file.go" +String id=48 + data="runtime/trace.Start.func1" +String id=49 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" +String id=50 + data="runtime.gcStart" +String id=51 + data="runtime.traceLocker.GCSweepSpan" +String id=52 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" +String id=53 + data="runtime.(*sweepLocked).sweep" +String id=54 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgcsweep.go" +String id=55 + data="runtime.(*mcentral).cacheSpan" +String id=56 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mcentral.go" +String id=57 + data="runtime.(*mcache).refill" +String id=58 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mcache.go" +String id=59 + data="runtime.(*mcache).nextFree" +String id=60 + data="runtime.gopark" +String id=61 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" +String id=62 + data="runtime.gcBgMarkWorker" +String id=63 + data="runtime.gcParkAssist" +String id=64 + data="runtime.systemstack_switch" +String id=65 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/asm_amd64.s" +String id=66 + data="runtime.traceLocker.GCMarkAssistStart" +String id=67 + data="runtime.(*mheap).alloc" +String id=68 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mheap.go" +String id=69 + data="runtime.(*mcache).allocLarge" +String id=70 + data="runtime.chanrecv1" +String id=71 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" +String id=72 + data="runtime.(*wakeableSleep).sleep" +String id=73 + data="runtime.(*mcentral).grow" +String id=74 + data="runtime.traceLocker.Gomaxprocs" +String id=75 + data="runtime.traceLocker.stack" +String id=76 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2event.go" +String id=77 + data="runtime.traceLocker.GoUnpark" +String id=78 + data="runtime.injectglist" +String id=79 + data="runtime.gcWakeAllAssists" diff --git a/src/internal/trace/testdata/tests/go122-go-create-without-running-g.test b/src/internal/trace/testdata/tests/go122-go-create-without-running-g.test new file mode 100644 index 0000000000..494c444ca3 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-go-create-without-running-g.test @@ -0,0 +1,17 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=17 +ProcStatus dt=1 p=0 pstatus=1 +GoCreate dt=1 new_g=5 new_stack=0 stack=0 +GoStart dt=1 g=5 g_seq=1 +GoStop dt=1 reason_string=1 stack=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=12 +Strings +String id=1 + data="whatever" diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-ambiguous.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-ambiguous.test new file mode 100644 index 0000000000..0d88af4d61 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-ambiguous.test @@ -0,0 +1,21 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=21 +ProcStatus dt=0 p=0 pstatus=1 +GoStatus dt=0 g=1 m=0 gstatus=2 +GoSyscallBegin dt=0 p_seq=1 stack=0 +GoSyscallEnd dt=0 +GoSyscallBegin dt=0 p_seq=2 stack=0 +GoSyscallEndBlocked dt=0 +EventBatch gen=1 m=1 time=0 size=14 +ProcStatus dt=0 p=2 pstatus=1 +GoStatus dt=0 g=2 m=1 gstatus=2 +ProcSteal dt=0 p=0 p_seq=3 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test new file mode 100644 index 0000000000..bbfc9cc877 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test @@ -0,0 +1,17 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=11 +ProcStatus dt=1 p=1 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=3 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=9 +ProcStatus dt=1 p=0 pstatus=4 +ProcSteal dt=1 p=0 p_seq=1 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test new file mode 100644 index 0000000000..8e291327cf --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test @@ -0,0 +1,18 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=15 +GoStatus dt=1 g=1 m=0 gstatus=3 +ProcStatus dt=1 p=1 pstatus=2 +ProcStart dt=1 p=1 p_seq=1 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=9 +ProcStatus dt=1 p=0 pstatus=4 +ProcSteal dt=1 p=0 p_seq=1 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test new file mode 100644 index 0000000000..3b26e8f13f --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test @@ -0,0 +1,20 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=15 +GoStatus dt=1 g=1 m=0 gstatus=3 +ProcStatus dt=1 p=1 pstatus=2 +ProcStart dt=1 p=1 p_seq=1 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=18 +ProcStatus dt=1 p=2 pstatus=1 +GoStatus dt=1 g=2 m=1 gstatus=2 +ProcStatus dt=1 p=0 pstatus=4 +ProcSteal dt=1 p=0 p_seq=1 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary.test new file mode 100644 index 0000000000..133d8a5447 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-gen-boundary.test @@ -0,0 +1,19 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=11 +ProcStatus dt=1 p=1 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=3 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=18 +ProcStatus dt=1 p=2 pstatus=1 +GoStatus dt=1 g=2 m=1 gstatus=2 +ProcStatus dt=1 p=0 pstatus=4 +ProcSteal dt=1 p=0 p_seq=1 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test new file mode 100644 index 0000000000..fa68c824b9 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test @@ -0,0 +1,19 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=23 +ProcStatus dt=1 p=1 pstatus=2 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=2 +GoSyscallBegin dt=1 p_seq=1 stack=0 +ProcStart dt=1 p=1 p_seq=1 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=5 +ProcSteal dt=1 p=0 p_seq=2 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test new file mode 100644 index 0000000000..85c19fcf05 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test @@ -0,0 +1,21 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=23 +ProcStatus dt=1 p=1 pstatus=2 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=2 +GoSyscallBegin dt=1 p_seq=1 stack=0 +ProcStart dt=1 p=1 p_seq=1 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=14 +ProcStatus dt=1 p=2 pstatus=1 +GoStatus dt=1 g=2 m=1 gstatus=2 +ProcSteal dt=1 p=0 p_seq=2 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-self.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-self.test new file mode 100644 index 0000000000..6484eb6d35 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-self.test @@ -0,0 +1,17 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=24 +ProcStatus dt=0 p=0 pstatus=1 +GoStatus dt=0 g=1 m=0 gstatus=2 +GoSyscallBegin dt=0 p_seq=1 stack=0 +ProcSteal dt=0 p=0 p_seq=2 m=0 +ProcStart dt=0 p=0 p_seq=3 +GoSyscallEndBlocked dt=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test new file mode 100644 index 0000000000..d33872287d --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test @@ -0,0 +1,17 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=15 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=2 +GoSyscallBegin dt=1 p_seq=1 stack=0 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=5 +ProcSteal dt=1 p=0 p_seq=2 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple.test new file mode 100644 index 0000000000..a1f9db41d4 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-simple.test @@ -0,0 +1,19 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=15 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=2 +GoSyscallBegin dt=1 p_seq=1 stack=0 +GoSyscallEndBlocked dt=1 +EventBatch gen=1 m=1 time=0 size=14 +ProcStatus dt=1 p=2 pstatus=1 +GoStatus dt=1 g=2 m=1 gstatus=2 +ProcSteal dt=1 p=0 p_seq=2 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test new file mode 100644 index 0000000000..58c41c5549 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test @@ -0,0 +1,15 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=9 +ProcStatus dt=1 p=0 pstatus=4 +ProcSteal dt=1 p=0 p_seq=1 m=1 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +GoStatus dt=1 g=1 m=1 gstatus=3 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testdata/tests/go122-task-across-generations.test b/src/internal/trace/testdata/tests/go122-task-across-generations.test new file mode 100644 index 0000000000..0b8abd7346 --- /dev/null +++ b/src/internal/trace/testdata/tests/go122-task-across-generations.test @@ -0,0 +1,26 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=15 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=2 +UserTaskBegin dt=1 task=2 parent_task=0 name_string=1 stack=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=11 +Strings +String id=1 + data="my task" +EventBatch gen=2 m=0 time=5 size=13 +ProcStatus dt=1 p=0 pstatus=1 +GoStatus dt=1 g=1 m=0 gstatus=2 +UserTaskEnd dt=1 task=2 stack=0 +EventBatch gen=2 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=2 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=2 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/testtrace/expectation.go b/src/internal/trace/testtrace/expectation.go new file mode 100644 index 0000000000..3e5394a7e4 --- /dev/null +++ b/src/internal/trace/testtrace/expectation.go @@ -0,0 +1,81 @@ +// 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. + +package testtrace + +import ( + "bufio" + "bytes" + "fmt" + "regexp" + "strconv" + "strings" +) + +// Expectation represents the expected result of some operation. +type Expectation struct { + failure bool + errorMatcher *regexp.Regexp +} + +// ExpectSuccess returns an Expectation that trivially expects success. +func ExpectSuccess() *Expectation { + return new(Expectation) +} + +// Check validates whether err conforms to the expectation. Returns +// an error if it does not conform. +// +// Conformance means that if failure is true, then err must be non-nil. +// If err is non-nil, then it must match errorMatcher. +func (e *Expectation) Check(err error) error { + if !e.failure && err != nil { + return fmt.Errorf("unexpected error while reading the trace: %v", err) + } + if e.failure && err == nil { + return fmt.Errorf("expected error while reading the trace: want something matching %q, got none", e.errorMatcher) + } + if e.failure && err != nil && !e.errorMatcher.MatchString(err.Error()) { + return fmt.Errorf("unexpected error while reading the trace: want something matching %q, got %s", e.errorMatcher, err.Error()) + } + return nil +} + +// ParseExpectation parses the serialized form of an Expectation. +func ParseExpectation(data []byte) (*Expectation, error) { + exp := new(Expectation) + s := bufio.NewScanner(bytes.NewReader(data)) + if s.Scan() { + c := strings.SplitN(s.Text(), " ", 2) + switch c[0] { + case "SUCCESS": + case "FAILURE": + exp.failure = true + if len(c) != 2 { + return exp, fmt.Errorf("bad header line for FAILURE: %q", s.Text()) + } + matcher, err := parseMatcher(c[1]) + if err != nil { + return exp, err + } + exp.errorMatcher = matcher + default: + return exp, fmt.Errorf("bad header line: %q", s.Text()) + } + return exp, nil + } + return exp, s.Err() +} + +func parseMatcher(quoted string) (*regexp.Regexp, error) { + pattern, err := strconv.Unquote(quoted) + if err != nil { + return nil, fmt.Errorf("malformed pattern: not correctly quoted: %s: %v", quoted, err) + } + matcher, err := regexp.Compile(pattern) + if err != nil { + return nil, fmt.Errorf("malformed pattern: not a valid regexp: %s: %v", pattern, err) + } + return matcher, nil +} diff --git a/src/internal/trace/testtrace/format.go b/src/internal/trace/testtrace/format.go new file mode 100644 index 0000000000..82b69340ae --- /dev/null +++ b/src/internal/trace/testtrace/format.go @@ -0,0 +1,56 @@ +// 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. + +package testtrace + +import ( + "bytes" + "fmt" + "internal/trace/raw" + "internal/txtar" + "io" +) + +// ParseFile parses a test file generated by the testgen package. +func ParseFile(testPath string) (io.Reader, *Expectation, error) { + ar, err := txtar.ParseFile(testPath) + if err != nil { + return nil, nil, fmt.Errorf("failed to read test file for %s: %v", testPath, err) + } + if len(ar.Files) != 2 { + return nil, nil, fmt.Errorf("malformed test %s: wrong number of files", testPath) + } + if ar.Files[0].Name != "expect" { + return nil, nil, fmt.Errorf("malformed test %s: bad filename %s", testPath, ar.Files[0].Name) + } + if ar.Files[1].Name != "trace" { + return nil, nil, fmt.Errorf("malformed test %s: bad filename %s", testPath, ar.Files[1].Name) + } + tr, err := raw.NewTextReader(bytes.NewReader(ar.Files[1].Data)) + if err != nil { + return nil, nil, fmt.Errorf("malformed test %s: bad trace file: %v", testPath, err) + } + var buf bytes.Buffer + tw, err := raw.NewWriter(&buf, tr.Version()) + if err != nil { + return nil, nil, fmt.Errorf("failed to create trace byte writer: %v", err) + } + for { + ev, err := tr.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + return nil, nil, fmt.Errorf("malformed test %s: bad trace file: %v", testPath, err) + } + if err := tw.WriteEvent(ev); err != nil { + return nil, nil, fmt.Errorf("internal error during %s: failed to write trace bytes: %v", testPath, err) + } + } + exp, err := ParseExpectation(ar.Files[0].Data) + if err != nil { + return nil, nil, fmt.Errorf("internal error during %s: failed to parse expectation %q: %v", testPath, string(ar.Files[0].Data), err) + } + return &buf, exp, nil +} diff --git a/src/internal/trace/testtrace/validation.go b/src/internal/trace/testtrace/validation.go new file mode 100644 index 0000000000..ec492110e2 --- /dev/null +++ b/src/internal/trace/testtrace/validation.go @@ -0,0 +1,379 @@ +// 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. + +package testtrace + +import ( + "errors" + "fmt" + "internal/trace" + "slices" + "strings" +) + +// Validator is a type used for validating a stream of trace.Events. +type Validator struct { + lastTs trace.Time + gs map[trace.GoID]*goState + ps map[trace.ProcID]*procState + ms map[trace.ThreadID]*schedContext + ranges map[trace.ResourceID][]string + tasks map[trace.TaskID]string + seenSync bool + Go121 bool +} + +type schedContext struct { + M trace.ThreadID + P trace.ProcID + G trace.GoID +} + +type goState struct { + state trace.GoState + binding *schedContext +} + +type procState struct { + state trace.ProcState + binding *schedContext +} + +// NewValidator creates a new Validator. +func NewValidator() *Validator { + return &Validator{ + gs: make(map[trace.GoID]*goState), + ps: make(map[trace.ProcID]*procState), + ms: make(map[trace.ThreadID]*schedContext), + ranges: make(map[trace.ResourceID][]string), + tasks: make(map[trace.TaskID]string), + } +} + +// Event validates ev as the next event in a stream of trace.Events. +// +// Returns an error if validation fails. +func (v *Validator) Event(ev trace.Event) error { + e := new(errAccumulator) + + // Validate timestamp order. + if v.lastTs != 0 { + if ev.Time() <= v.lastTs { + e.Errorf("timestamp out-of-order for %+v", ev) + } else { + v.lastTs = ev.Time() + } + } else { + v.lastTs = ev.Time() + } + + // Validate event stack. + checkStack(e, ev.Stack()) + + switch ev.Kind() { + case trace.EventSync: + // Just record that we've seen a Sync at some point. + v.seenSync = true + case trace.EventMetric: + m := ev.Metric() + if !strings.Contains(m.Name, ":") { + // Should have a ":" as per runtime/metrics convention. + e.Errorf("invalid metric name %q", m.Name) + } + // Make sure the value is OK. + if m.Value.Kind() == trace.ValueBad { + e.Errorf("invalid value") + } + switch m.Value.Kind() { + case trace.ValueUint64: + // Just make sure it doesn't panic. + _ = m.Value.Uint64() + } + case trace.EventLabel: + l := ev.Label() + + // Check label. + if l.Label == "" { + e.Errorf("invalid label %q", l.Label) + } + + // Check label resource. + if l.Resource.Kind == trace.ResourceNone { + e.Errorf("label resource none") + } + switch l.Resource.Kind { + case trace.ResourceGoroutine: + id := l.Resource.Goroutine() + if _, ok := v.gs[id]; !ok { + e.Errorf("label for invalid goroutine %d", id) + } + case trace.ResourceProc: + id := l.Resource.Proc() + if _, ok := v.ps[id]; !ok { + e.Errorf("label for invalid proc %d", id) + } + case trace.ResourceThread: + id := l.Resource.Thread() + if _, ok := v.ms[id]; !ok { + e.Errorf("label for invalid thread %d", id) + } + } + case trace.EventStackSample: + // Not much to check here. It's basically a sched context and a stack. + // The sched context is also not guaranteed to align with other events. + // We already checked the stack above. + case trace.EventStateTransition: + // Validate state transitions. + // + // TODO(mknyszek): A lot of logic is duplicated between goroutines and procs. + // The two are intentionally handled identically; from the perspective of the + // API, resources all have the same general properties. Consider making this + // code generic over resources and implementing validation just once. + tr := ev.StateTransition() + checkStack(e, tr.Stack) + switch tr.Resource.Kind { + case trace.ResourceGoroutine: + // Basic state transition validation. + id := tr.Resource.Goroutine() + old, new := tr.Goroutine() + if new == trace.GoUndetermined { + e.Errorf("transition to undetermined state for goroutine %d", id) + } + if v.seenSync && old == trace.GoUndetermined { + e.Errorf("undetermined goroutine %d after first global sync", id) + } + if new == trace.GoNotExist && v.hasAnyRange(trace.MakeResourceID(id)) { + e.Errorf("goroutine %d died with active ranges", id) + } + state, ok := v.gs[id] + if ok { + if old != state.state { + e.Errorf("bad old state for goroutine %d: got %s, want %s", id, old, state.state) + } + state.state = new + } else { + if old != trace.GoUndetermined && old != trace.GoNotExist { + e.Errorf("bad old state for unregistered goroutine %d: %s", id, old) + } + state = &goState{state: new} + v.gs[id] = state + } + // Validate sched context. + if new.Executing() { + ctx := v.getOrCreateThread(e, ev, ev.Thread()) + if ctx != nil { + if ctx.G != trace.NoGoroutine && ctx.G != id { + e.Errorf("tried to run goroutine %d when one was already executing (%d) on thread %d", id, ctx.G, ev.Thread()) + } + ctx.G = id + state.binding = ctx + } + } else if old.Executing() && !new.Executing() { + if tr.Stack != ev.Stack() { + // This is a case where the transition is happening to a goroutine that is also executing, so + // these two stacks should always match. + e.Errorf("StateTransition.Stack doesn't match Event.Stack") + } + ctx := state.binding + if ctx != nil { + if ctx.G != id { + e.Errorf("tried to stop goroutine %d when it wasn't currently executing (currently executing %d) on thread %d", id, ctx.G, ev.Thread()) + } + ctx.G = trace.NoGoroutine + state.binding = nil + } else { + e.Errorf("stopping goroutine %d not bound to any active context", id) + } + } + case trace.ResourceProc: + // Basic state transition validation. + id := tr.Resource.Proc() + old, new := tr.Proc() + if new == trace.ProcUndetermined { + e.Errorf("transition to undetermined state for proc %d", id) + } + if v.seenSync && old == trace.ProcUndetermined { + e.Errorf("undetermined proc %d after first global sync", id) + } + if new == trace.ProcNotExist && v.hasAnyRange(trace.MakeResourceID(id)) { + e.Errorf("proc %d died with active ranges", id) + } + state, ok := v.ps[id] + if ok { + if old != state.state { + e.Errorf("bad old state for proc %d: got %s, want %s", id, old, state.state) + } + state.state = new + } else { + if old != trace.ProcUndetermined && old != trace.ProcNotExist { + e.Errorf("bad old state for unregistered proc %d: %s", id, old) + } + state = &procState{state: new} + v.ps[id] = state + } + // Validate sched context. + if new.Executing() { + ctx := v.getOrCreateThread(e, ev, ev.Thread()) + if ctx != nil { + if ctx.P != trace.NoProc && ctx.P != id { + e.Errorf("tried to run proc %d when one was already executing (%d) on thread %d", id, ctx.P, ev.Thread()) + } + ctx.P = id + state.binding = ctx + } + } else if old.Executing() && !new.Executing() { + ctx := state.binding + if ctx != nil { + if ctx.P != id { + e.Errorf("tried to stop proc %d when it wasn't currently executing (currently executing %d) on thread %d", id, ctx.P, ctx.M) + } + ctx.P = trace.NoProc + state.binding = nil + } else { + e.Errorf("stopping proc %d not bound to any active context", id) + } + } + } + case trace.EventRangeBegin, trace.EventRangeActive, trace.EventRangeEnd: + // Validate ranges. + r := ev.Range() + switch ev.Kind() { + case trace.EventRangeBegin: + if v.hasRange(r.Scope, r.Name) { + e.Errorf("already active range %q on %v begun again", r.Name, r.Scope) + } + v.addRange(r.Scope, r.Name) + case trace.EventRangeActive: + if !v.hasRange(r.Scope, r.Name) { + v.addRange(r.Scope, r.Name) + } + case trace.EventRangeEnd: + if !v.hasRange(r.Scope, r.Name) { + e.Errorf("inactive range %q on %v ended", r.Name, r.Scope) + } + v.deleteRange(r.Scope, r.Name) + } + case trace.EventTaskBegin: + // Validate task begin. + t := ev.Task() + if t.ID == trace.NoTask || t.ID == trace.BackgroundTask { + // The background task should never have an event emitted for it. + e.Errorf("found invalid task ID for task of type %s", t.Type) + } + if t.Parent == trace.BackgroundTask { + // It's not possible for a task to be a subtask of the background task. + e.Errorf("found background task as the parent for task of type %s", t.Type) + } + // N.B. Don't check the task type. Empty string is a valid task type. + v.tasks[t.ID] = t.Type + case trace.EventTaskEnd: + // Validate task end. + // We can see a task end without a begin, so ignore a task without information. + // Instead, if we've seen the task begin, just make sure the task end lines up. + t := ev.Task() + if typ, ok := v.tasks[t.ID]; ok { + if t.Type != typ { + e.Errorf("task end type %q doesn't match task start type %q for task %d", t.Type, typ, t.ID) + } + delete(v.tasks, t.ID) + } + case trace.EventLog: + // There's really not much here to check, except that we can + // generate a Log. The category and message are entirely user-created, + // so we can't make any assumptions as to what they are. We also + // can't validate the task, because proving the task's existence is very + // much best-effort. + _ = ev.Log() + } + return e.Errors() +} + +func (v *Validator) hasRange(r trace.ResourceID, name string) bool { + ranges, ok := v.ranges[r] + return ok && slices.Contains(ranges, name) +} + +func (v *Validator) addRange(r trace.ResourceID, name string) { + ranges, _ := v.ranges[r] + ranges = append(ranges, name) + v.ranges[r] = ranges +} + +func (v *Validator) hasAnyRange(r trace.ResourceID) bool { + ranges, ok := v.ranges[r] + return ok && len(ranges) != 0 +} + +func (v *Validator) deleteRange(r trace.ResourceID, name string) { + ranges, ok := v.ranges[r] + if !ok { + return + } + i := slices.Index(ranges, name) + if i < 0 { + return + } + v.ranges[r] = slices.Delete(ranges, i, i+1) +} + +func (v *Validator) getOrCreateThread(e *errAccumulator, ev trace.Event, m trace.ThreadID) *schedContext { + lenient := func() bool { + // Be lenient about GoUndetermined -> GoSyscall transitions if they + // originate from an old trace. These transitions lack thread + // information in trace formats older than 1.22. + if !v.Go121 { + return false + } + if ev.Kind() != trace.EventStateTransition { + return false + } + tr := ev.StateTransition() + if tr.Resource.Kind != trace.ResourceGoroutine { + return false + } + from, to := tr.Goroutine() + return from == trace.GoUndetermined && to == trace.GoSyscall + } + if m == trace.NoThread && !lenient() { + e.Errorf("must have thread, but thread ID is none") + return nil + } + s, ok := v.ms[m] + if !ok { + s = &schedContext{M: m, P: trace.NoProc, G: trace.NoGoroutine} + v.ms[m] = s + return s + } + return s +} + +func checkStack(e *errAccumulator, stk trace.Stack) { + // Check for non-empty values, but we also check for crashes due to incorrect validation. + i := 0 + stk.Frames(func(f trace.StackFrame) bool { + if i == 0 { + // Allow for one fully zero stack. + // + // TODO(mknyszek): Investigate why that happens. + return true + } + if f.Func == "" || f.File == "" || f.PC == 0 || f.Line == 0 { + e.Errorf("invalid stack frame %#v: missing information", f) + } + i++ + return true + }) +} + +type errAccumulator struct { + errs []error +} + +func (e *errAccumulator) Errorf(f string, args ...any) { + e.errs = append(e.errs, fmt.Errorf(f, args...)) +} + +func (e *errAccumulator) Errors() error { + return errors.Join(e.errs...) +} diff --git a/src/internal/trace/trace_test.go b/src/internal/trace/trace_test.go new file mode 100644 index 0000000000..7dc5cbb89d --- /dev/null +++ b/src/internal/trace/trace_test.go @@ -0,0 +1,629 @@ +// 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. + +package trace_test + +import ( + "bufio" + "bytes" + "fmt" + "internal/race" + "internal/testenv" + "internal/trace" + "internal/trace/testtrace" + "io" + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestTraceAnnotations(t *testing.T) { + testTraceProg(t, "annotations.go", func(t *testing.T, tb, _ []byte, _ bool) { + type evDesc struct { + kind trace.EventKind + task trace.TaskID + args []string + } + want := []evDesc{ + {trace.EventTaskBegin, trace.TaskID(1), []string{"task0"}}, + {trace.EventRegionBegin, trace.TaskID(1), []string{"region0"}}, + {trace.EventRegionBegin, trace.TaskID(1), []string{"region1"}}, + {trace.EventLog, trace.TaskID(1), []string{"key0", "0123456789abcdef"}}, + {trace.EventRegionEnd, trace.TaskID(1), []string{"region1"}}, + {trace.EventRegionEnd, trace.TaskID(1), []string{"region0"}}, + {trace.EventTaskEnd, trace.TaskID(1), []string{"task0"}}, + // Currently, pre-existing region is not recorded to avoid allocations. + {trace.EventRegionBegin, trace.BackgroundTask, []string{"post-existing region"}}, + } + r, err := trace.NewReader(bytes.NewReader(tb)) + if err != nil { + t.Error(err) + } + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + for i, wantEv := range want { + if wantEv.kind != ev.Kind() { + continue + } + match := false + switch ev.Kind() { + case trace.EventTaskBegin, trace.EventTaskEnd: + task := ev.Task() + match = task.ID == wantEv.task && task.Type == wantEv.args[0] + case trace.EventRegionBegin, trace.EventRegionEnd: + reg := ev.Region() + match = reg.Task == wantEv.task && reg.Type == wantEv.args[0] + case trace.EventLog: + log := ev.Log() + match = log.Task == wantEv.task && log.Category == wantEv.args[0] && log.Message == wantEv.args[1] + } + if match { + want[i] = want[len(want)-1] + want = want[:len(want)-1] + break + } + } + } + if len(want) != 0 { + for _, ev := range want { + t.Errorf("no match for %s TaskID=%d Args=%#v", ev.kind, ev.task, ev.args) + } + } + }) +} + +func TestTraceAnnotationsStress(t *testing.T) { + testTraceProg(t, "annotations-stress.go", nil) +} + +func TestTraceCgoCallback(t *testing.T) { + testenv.MustHaveCGO(t) + + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("cgo callback test requires pthreads and is not supported on %s", runtime.GOOS) + } + testTraceProg(t, "cgo-callback.go", nil) +} + +func TestTraceCPUProfile(t *testing.T) { + testTraceProg(t, "cpu-profile.go", func(t *testing.T, tb, stderr []byte, _ bool) { + // Parse stderr which has a CPU profile summary, if everything went well. + // (If it didn't, we shouldn't even make it here.) + scanner := bufio.NewScanner(bytes.NewReader(stderr)) + pprofSamples := 0 + pprofStacks := make(map[string]int) + for scanner.Scan() { + var stack string + var samples int + _, err := fmt.Sscanf(scanner.Text(), "%s\t%d", &stack, &samples) + if err != nil { + t.Fatalf("failed to parse CPU profile summary in stderr: %s\n\tfull:\n%s", scanner.Text(), stderr) + } + pprofStacks[stack] = samples + pprofSamples += samples + } + if err := scanner.Err(); err != nil { + t.Fatalf("failed to parse CPU profile summary in stderr: %v", err) + } + if pprofSamples == 0 { + t.Skip("CPU profile did not include any samples while tracing was active") + } + + // Examine the execution tracer's view of the CPU profile samples. Filter it + // to only include samples from the single test goroutine. Use the goroutine + // ID that was recorded in the events: that should reflect getg().m.curg, + // same as the profiler's labels (even when the M is using its g0 stack). + totalTraceSamples := 0 + traceSamples := 0 + traceStacks := make(map[string]int) + r, err := trace.NewReader(bytes.NewReader(tb)) + if err != nil { + t.Error(err) + } + var hogRegion *trace.Event + var hogRegionClosed bool + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + if ev.Kind() == trace.EventRegionBegin && ev.Region().Type == "cpuHogger" { + hogRegion = &ev + } + if ev.Kind() == trace.EventStackSample { + totalTraceSamples++ + if hogRegion != nil && ev.Goroutine() == hogRegion.Goroutine() { + traceSamples++ + var fns []string + ev.Stack().Frames(func(frame trace.StackFrame) bool { + if frame.Func != "runtime.goexit" { + fns = append(fns, fmt.Sprintf("%s:%d", frame.Func, frame.Line)) + } + return true + }) + stack := strings.Join(fns, "|") + traceStacks[stack]++ + } + } + if ev.Kind() == trace.EventRegionEnd && ev.Region().Type == "cpuHogger" { + hogRegionClosed = true + } + } + if hogRegion == nil { + t.Fatalf("execution trace did not identify cpuHogger goroutine") + } else if !hogRegionClosed { + t.Fatalf("execution trace did not close cpuHogger region") + } + + // The execution trace may drop CPU profile samples if the profiling buffer + // overflows. Based on the size of profBufWordCount, that takes a bit over + // 1900 CPU samples or 19 thread-seconds at a 100 Hz sample rate. If we've + // hit that case, then we definitely have at least one full buffer's worth + // of CPU samples, so we'll call that success. + overflowed := totalTraceSamples >= 1900 + if traceSamples < pprofSamples { + t.Logf("execution trace did not include all CPU profile samples; %d in profile, %d in trace", pprofSamples, traceSamples) + if !overflowed { + t.Fail() + } + } + + for stack, traceSamples := range traceStacks { + pprofSamples := pprofStacks[stack] + delete(pprofStacks, stack) + if traceSamples < pprofSamples { + t.Logf("execution trace did not include all CPU profile samples for stack %q; %d in profile, %d in trace", + stack, pprofSamples, traceSamples) + if !overflowed { + t.Fail() + } + } + } + for stack, pprofSamples := range pprofStacks { + t.Logf("CPU profile included %d samples at stack %q not present in execution trace", pprofSamples, stack) + if !overflowed { + t.Fail() + } + } + + if t.Failed() { + t.Logf("execution trace CPU samples:") + for stack, samples := range traceStacks { + t.Logf("%d: %q", samples, stack) + } + t.Logf("CPU profile:\n%s", stderr) + } + }) +} + +func TestTraceFutileWakeup(t *testing.T) { + testTraceProg(t, "futile-wakeup.go", func(t *testing.T, tb, _ []byte, _ bool) { + // Check to make sure that no goroutine in the "special" trace region + // ends up blocking, unblocking, then immediately blocking again. + // + // The goroutines are careful to call runtime.Gosched in between blocking, + // so there should never be a clean block/unblock on the goroutine unless + // the runtime was generating extraneous events. + const ( + entered = iota + blocked + runnable + running + ) + gs := make(map[trace.GoID]int) + seenSpecialGoroutines := false + r, err := trace.NewReader(bytes.NewReader(tb)) + if err != nil { + t.Error(err) + } + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + // Only track goroutines in the special region we control, so runtime + // goroutines don't interfere (it's totally valid in traces for a + // goroutine to block, run, and block again; that's not what we care about). + if ev.Kind() == trace.EventRegionBegin && ev.Region().Type == "special" { + seenSpecialGoroutines = true + gs[ev.Goroutine()] = entered + } + if ev.Kind() == trace.EventRegionEnd && ev.Region().Type == "special" { + delete(gs, ev.Goroutine()) + } + // Track state transitions for goroutines we care about. + // + // The goroutines we care about will advance through the state machine + // of entered -> blocked -> runnable -> running. If in the running state + // we block, then we have a futile wakeup. Because of the runtime.Gosched + // on these specially marked goroutines, we should end up back in runnable + // first. If at any point we go to a different state, switch back to entered + // and wait for the next time the goroutine blocks. + if ev.Kind() != trace.EventStateTransition { + continue + } + st := ev.StateTransition() + if st.Resource.Kind != trace.ResourceGoroutine { + continue + } + id := st.Resource.Goroutine() + state, ok := gs[id] + if !ok { + continue + } + _, new := st.Goroutine() + switch state { + case entered: + if new == trace.GoWaiting { + state = blocked + } else { + state = entered + } + case blocked: + if new == trace.GoRunnable { + state = runnable + } else { + state = entered + } + case runnable: + if new == trace.GoRunning { + state = running + } else { + state = entered + } + case running: + if new == trace.GoWaiting { + t.Fatalf("found futile wakeup on goroutine %d", id) + } else { + state = entered + } + } + gs[id] = state + } + if !seenSpecialGoroutines { + t.Fatal("did not see a goroutine in a the region 'special'") + } + }) +} + +func TestTraceGCStress(t *testing.T) { + testTraceProg(t, "gc-stress.go", nil) +} + +func TestTraceGOMAXPROCS(t *testing.T) { + testTraceProg(t, "gomaxprocs.go", nil) +} + +func TestTraceStacks(t *testing.T) { + testTraceProg(t, "stacks.go", func(t *testing.T, tb, _ []byte, stress bool) { + type frame struct { + fn string + line int + } + type evDesc struct { + kind trace.EventKind + match string + frames []frame + } + // mainLine is the line number of `func main()` in testprog/stacks.go. + const mainLine = 21 + want := []evDesc{ + {trace.EventStateTransition, "Goroutine Running->Runnable", []frame{ + {"main.main", mainLine + 82}, + }}, + {trace.EventStateTransition, "Goroutine NotExist->Runnable", []frame{ + {"main.main", mainLine + 11}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"runtime.block", 0}, + {"main.main.func1", 0}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"runtime.chansend1", 0}, + {"main.main.func2", 0}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"runtime.chanrecv1", 0}, + {"main.main.func3", 0}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"runtime.chanrecv1", 0}, + {"main.main.func4", 0}, + }}, + {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ + {"runtime.chansend1", 0}, + {"main.main", mainLine + 84}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"runtime.chansend1", 0}, + {"main.main.func5", 0}, + }}, + {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ + {"runtime.chanrecv1", 0}, + {"main.main", mainLine + 85}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"runtime.selectgo", 0}, + {"main.main.func6", 0}, + }}, + {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ + {"runtime.selectgo", 0}, + {"main.main", mainLine + 86}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"sync.(*Mutex).Lock", 0}, + {"main.main.func7", 0}, + }}, + {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ + {"sync.(*Mutex).Unlock", 0}, + {"main.main", 0}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"sync.(*WaitGroup).Wait", 0}, + {"main.main.func8", 0}, + }}, + {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ + {"sync.(*WaitGroup).Add", 0}, + {"sync.(*WaitGroup).Done", 0}, + {"main.main", mainLine + 91}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"sync.(*Cond).Wait", 0}, + {"main.main.func9", 0}, + }}, + {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ + {"sync.(*Cond).Signal", 0}, + {"main.main", 0}, + }}, + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"time.Sleep", 0}, + {"main.main", 0}, + }}, + {trace.EventMetric, "/sched/gomaxprocs:threads", []frame{ + {"runtime.startTheWorld", 0}, // this is when the current gomaxprocs is logged. + {"runtime.startTheWorldGC", 0}, + {"runtime.GOMAXPROCS", 0}, + {"main.main", 0}, + }}, + } + if !stress { + // Only check for this stack if !stress because traceAdvance alone could + // allocate enough memory to trigger a GC if called frequently enough. + // This might cause the runtime.GC call we're trying to match against to + // coalesce with an active GC triggered this by traceAdvance. In that case + // we won't have an EventRangeBegin event that matches the stace trace we're + // looking for, since runtime.GC will not have triggered the GC. + gcEv := evDesc{trace.EventRangeBegin, "GC concurrent mark phase", []frame{ + {"runtime.GC", 0}, + {"main.main", 0}, + }} + want = append(want, gcEv) + } + if runtime.GOOS != "windows" && runtime.GOOS != "plan9" { + want = append(want, []evDesc{ + {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ + {"internal/poll.(*FD).Accept", 0}, + {"net.(*netFD).accept", 0}, + {"net.(*TCPListener).accept", 0}, + {"net.(*TCPListener).Accept", 0}, + {"main.main.func10", 0}, + }}, + {trace.EventStateTransition, "Goroutine Running->Syscall", []frame{ + {"syscall.read", 0}, + {"syscall.Read", 0}, + {"internal/poll.ignoringEINTRIO", 0}, + {"internal/poll.(*FD).Read", 0}, + {"os.(*File).read", 0}, + {"os.(*File).Read", 0}, + {"main.main.func11", 0}, + }}, + }...) + } + stackMatches := func(stk trace.Stack, frames []frame) bool { + i := 0 + match := true + stk.Frames(func(f trace.StackFrame) bool { + if f.Func != frames[i].fn { + match = false + return false + } + if line := uint64(frames[i].line); line != 0 && line != f.Line { + match = false + return false + } + i++ + return true + }) + return match + } + r, err := trace.NewReader(bytes.NewReader(tb)) + if err != nil { + t.Error(err) + } + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + for i, wantEv := range want { + if wantEv.kind != ev.Kind() { + continue + } + match := false + switch ev.Kind() { + case trace.EventStateTransition: + st := ev.StateTransition() + str := "" + switch st.Resource.Kind { + case trace.ResourceGoroutine: + old, new := st.Goroutine() + str = fmt.Sprintf("%s %s->%s", st.Resource.Kind, old, new) + } + match = str == wantEv.match + case trace.EventRangeBegin: + rng := ev.Range() + match = rng.Name == wantEv.match + case trace.EventMetric: + metric := ev.Metric() + match = metric.Name == wantEv.match + } + match = match && stackMatches(ev.Stack(), wantEv.frames) + if match { + want[i] = want[len(want)-1] + want = want[:len(want)-1] + break + } + } + } + if len(want) != 0 { + for _, ev := range want { + t.Errorf("no match for %s Match=%s Stack=%#v", ev.kind, ev.match, ev.frames) + } + } + }) +} + +func TestTraceStress(t *testing.T) { + switch runtime.GOOS { + case "js", "wasip1": + t.Skip("no os.Pipe on " + runtime.GOOS) + } + testTraceProg(t, "stress.go", nil) +} + +func TestTraceStressStartStop(t *testing.T) { + switch runtime.GOOS { + case "js", "wasip1": + t.Skip("no os.Pipe on " + runtime.GOOS) + } + testTraceProg(t, "stress-start-stop.go", nil) +} + +func TestTraceManyStartStop(t *testing.T) { + testTraceProg(t, "many-start-stop.go", nil) +} + +func TestTraceWaitOnPipe(t *testing.T) { + switch runtime.GOOS { + case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": + testTraceProg(t, "wait-on-pipe.go", nil) + return + } + t.Skip("no applicable syscall.Pipe on " + runtime.GOOS) +} + +func TestTraceIterPull(t *testing.T) { + testTraceProg(t, "iter-pull.go", nil) +} + +func testTraceProg(t *testing.T, progName string, extra func(t *testing.T, trace, stderr []byte, stress bool)) { + testenv.MustHaveGoRun(t) + + // Check if we're on a builder. + onBuilder := testenv.Builder() != "" + onOldBuilder := !strings.Contains(testenv.Builder(), "gotip") && !strings.Contains(testenv.Builder(), "go1") + + testPath := filepath.Join("./testdata/testprog", progName) + testName := progName + runTest := func(t *testing.T, stress bool, extraGODEBUG string) { + // Run the program and capture the trace, which is always written to stdout. + cmd := testenv.Command(t, testenv.GoToolPath(t), "run") + if race.Enabled { + cmd.Args = append(cmd.Args, "-race") + } + cmd.Args = append(cmd.Args, testPath) + cmd.Env = append(os.Environ(), "GOEXPERIMENT=rangefunc") + // Add a stack ownership check. This is cheap enough for testing. + godebug := "tracecheckstackownership=1" + if stress { + // Advance a generation constantly to stress the tracer. + godebug += ",traceadvanceperiod=0" + } + if extraGODEBUG != "" { + // Add extra GODEBUG flags. + godebug += "," + extraGODEBUG + } + cmd.Env = append(cmd.Env, "GODEBUG="+godebug) + + // Capture stdout and stderr. + // + // The protocol for these programs is that stdout contains the trace data + // and stderr is an expectation in string format. + var traceBuf, errBuf bytes.Buffer + cmd.Stdout = &traceBuf + cmd.Stderr = &errBuf + // Run the program. + if err := cmd.Run(); err != nil { + if errBuf.Len() != 0 { + t.Logf("stderr: %s", string(errBuf.Bytes())) + } + t.Fatal(err) + } + tb := traceBuf.Bytes() + + // Test the trace and the parser. + testReader(t, bytes.NewReader(tb), testtrace.ExpectSuccess()) + + // Run some extra validation. + if !t.Failed() && extra != nil { + extra(t, tb, errBuf.Bytes(), stress) + } + + // Dump some more information on failure. + if t.Failed() && onBuilder { + // Dump directly to the test log on the builder, since this + // data is critical for debugging and this is the only way + // we can currently make sure it's retained. + t.Log("found bad trace; dumping to test log...") + s := dumpTraceToText(t, tb) + if onOldBuilder && len(s) > 1<<20+512<<10 { + // The old build infrastructure truncates logs at ~2 MiB. + // Let's assume we're the only failure and give ourselves + // up to 1.5 MiB to dump the trace. + // + // TODO(mknyszek): Remove this when we've migrated off of + // the old infrastructure. + t.Logf("text trace too large to dump (%d bytes)", len(s)) + } else { + t.Log(s) + } + } else if t.Failed() || *dumpTraces { + // We asked to dump the trace or failed. Write the trace to a file. + t.Logf("wrote trace to file: %s", dumpTraceToFile(t, testName, stress, tb)) + } + } + t.Run("Default", func(t *testing.T) { + runTest(t, false, "") + }) + t.Run("Stress", func(t *testing.T) { + if testing.Short() { + t.Skip("skipping trace stress tests in short mode") + } + runTest(t, true, "") + }) + t.Run("AllocFree", func(t *testing.T) { + if testing.Short() { + t.Skip("skipping trace alloc/free tests in short mode") + } + runTest(t, false, "traceallocfree=1") + }) +} diff --git a/src/internal/trace/v2/base.go b/src/internal/trace/v2/base.go deleted file mode 100644 index 31e3c70f66..0000000000 --- a/src/internal/trace/v2/base.go +++ /dev/null @@ -1,274 +0,0 @@ -// 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. - -// This file contains data types that all implementations of the trace format -// parser need to provide to the rest of the package. - -package trace - -import ( - "fmt" - "math" - "strings" - - "internal/trace/v2/event" - "internal/trace/v2/event/go122" - "internal/trace/v2/version" -) - -// maxArgs is the maximum number of arguments for "plain" events, -// i.e. anything that could reasonably be represented as a baseEvent. -// -// TODO(mknyszek): This is only 6 instead of 5 because GoStatusStack -// has 5 arguments and needs to smuggle in a 6th. Figure out a way to -// shrink this in the future. -const maxArgs = 6 - -// timedEventArgs is an array that is able to hold the arguments for any -// timed event. -type timedEventArgs [maxArgs - 1]uint64 - -// baseEvent is the basic unprocessed event. This serves as a common -// fundamental data structure across. -type baseEvent struct { - typ event.Type - time Time - args timedEventArgs -} - -// extra returns a slice representing extra available space in args -// that the parser can use to pass data up into Event. -func (e *baseEvent) extra(v version.Version) []uint64 { - switch v { - case version.Go122: - return e.args[len(go122.Specs()[e.typ].Args)-1:] - } - panic(fmt.Sprintf("unsupported version: go 1.%d", v)) -} - -// evTable contains the per-generation data necessary to -// interpret an individual event. -type evTable struct { - freq frequency - strings dataTable[stringID, string] - stacks dataTable[stackID, stack] - pcs map[uint64]frame - - // extraStrings are strings that get generated during - // parsing but haven't come directly from the trace, so - // they don't appear in strings. - extraStrings []string - extraStringIDs map[string]extraStringID - nextExtra extraStringID - - // expData contains extra unparsed data that is accessible - // only to ExperimentEvent via an EventExperimental event. - expData map[event.Experiment]*ExperimentalData -} - -// addExtraString adds an extra string to the evTable and returns -// a unique ID for the string in the table. -func (t *evTable) addExtraString(s string) extraStringID { - if s == "" { - return 0 - } - if t.extraStringIDs == nil { - t.extraStringIDs = make(map[string]extraStringID) - } - if id, ok := t.extraStringIDs[s]; ok { - return id - } - t.nextExtra++ - id := t.nextExtra - t.extraStrings = append(t.extraStrings, s) - t.extraStringIDs[s] = id - return id -} - -// getExtraString returns the extra string for the provided ID. -// The ID must have been produced by addExtraString for this evTable. -func (t *evTable) getExtraString(id extraStringID) string { - if id == 0 { - return "" - } - return t.extraStrings[id-1] -} - -// dataTable is a mapping from EIs to Es. -type dataTable[EI ~uint64, E any] struct { - present []uint8 - dense []E - sparse map[EI]E -} - -// insert tries to add a mapping from id to s. -// -// Returns an error if a mapping for id already exists, regardless -// of whether or not s is the same in content. This should be used -// for validation during parsing. -func (d *dataTable[EI, E]) insert(id EI, data E) error { - if d.sparse == nil { - d.sparse = make(map[EI]E) - } - if existing, ok := d.get(id); ok { - return fmt.Errorf("multiple %Ts with the same ID: id=%d, new=%v, existing=%v", data, id, data, existing) - } - d.sparse[id] = data - return nil -} - -// compactify attempts to compact sparse into dense. -// -// This is intended to be called only once after insertions are done. -func (d *dataTable[EI, E]) compactify() { - if d.sparse == nil || len(d.dense) != 0 { - // Already compactified. - return - } - // Find the range of IDs. - maxID := EI(0) - minID := ^EI(0) - for id := range d.sparse { - if id > maxID { - maxID = id - } - if id < minID { - minID = id - } - } - if maxID >= math.MaxInt { - // We can't create a slice big enough to hold maxID elements - return - } - // We're willing to waste at most 2x memory. - if int(maxID-minID) > max(len(d.sparse), 2*len(d.sparse)) { - return - } - if int(minID) > len(d.sparse) { - return - } - size := int(maxID) + 1 - d.present = make([]uint8, (size+7)/8) - d.dense = make([]E, size) - for id, data := range d.sparse { - d.dense[id] = data - d.present[id/8] |= uint8(1) << (id % 8) - } - d.sparse = nil -} - -// get returns the E for id or false if it doesn't -// exist. This should be used for validation during parsing. -func (d *dataTable[EI, E]) get(id EI) (E, bool) { - if id == 0 { - return *new(E), true - } - if uint64(id) < uint64(len(d.dense)) { - if d.present[id/8]&(uint8(1)<<(id%8)) != 0 { - return d.dense[id], true - } - } else if d.sparse != nil { - if data, ok := d.sparse[id]; ok { - return data, true - } - } - return *new(E), false -} - -// forEach iterates over all ID/value pairs in the data table. -func (d *dataTable[EI, E]) forEach(yield func(EI, E) bool) bool { - for id, value := range d.dense { - if d.present[id/8]&(uint8(1)<<(id%8)) == 0 { - continue - } - if !yield(EI(id), value) { - return false - } - } - if d.sparse == nil { - return true - } - for id, value := range d.sparse { - if !yield(id, value) { - return false - } - } - return true -} - -// mustGet returns the E for id or panics if it fails. -// -// This should only be used if id has already been validated. -func (d *dataTable[EI, E]) mustGet(id EI) E { - data, ok := d.get(id) - if !ok { - panic(fmt.Sprintf("expected id %d in %T table", id, data)) - } - return data -} - -// frequency is nanoseconds per timestamp unit. -type frequency float64 - -// mul multiplies an unprocessed to produce a time in nanoseconds. -func (f frequency) mul(t timestamp) Time { - return Time(float64(t) * float64(f)) -} - -// stringID is an index into the string table for a generation. -type stringID uint64 - -// extraStringID is an index into the extra string table for a generation. -type extraStringID uint64 - -// stackID is an index into the stack table for a generation. -type stackID uint64 - -// cpuSample represents a CPU profiling sample captured by the trace. -type cpuSample struct { - schedCtx - time Time - stack stackID -} - -// asEvent produces a complete Event from a cpuSample. It needs -// the evTable from the generation that created it. -// -// We don't just store it as an Event in generation to minimize -// the amount of pointer data floating around. -func (s cpuSample) asEvent(table *evTable) Event { - // TODO(mknyszek): This is go122-specific, but shouldn't be. - // Generalize this in the future. - e := Event{ - table: table, - ctx: s.schedCtx, - base: baseEvent{ - typ: go122.EvCPUSample, - time: s.time, - }, - } - e.base.args[0] = uint64(s.stack) - return e -} - -// stack represents a goroutine stack sample. -type stack struct { - pcs []uint64 -} - -func (s stack) String() string { - var sb strings.Builder - for _, frame := range s.pcs { - fmt.Fprintf(&sb, "\t%#v\n", frame) - } - return sb.String() -} - -// frame represents a single stack frame. -type frame struct { - pc uint64 - funcID stringID - fileID stringID - line uint64 -} diff --git a/src/internal/trace/v2/batch.go b/src/internal/trace/v2/batch.go deleted file mode 100644 index 57c230fe02..0000000000 --- a/src/internal/trace/v2/batch.go +++ /dev/null @@ -1,111 +0,0 @@ -// 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. - -package trace - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - - "internal/trace/v2/event" - "internal/trace/v2/event/go122" -) - -// timestamp is an unprocessed timestamp. -type timestamp uint64 - -// batch represents a batch of trace events. -// It is unparsed except for its header. -type batch struct { - m ThreadID - time timestamp - data []byte - exp event.Experiment -} - -func (b *batch) isStringsBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStrings -} - -func (b *batch) isStacksBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStacks -} - -func (b *batch) isCPUSamplesBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvCPUSamples -} - -func (b *batch) isFreqBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvFrequency -} - -// readBatch reads the next full batch from r. -func readBatch(r interface { - io.Reader - io.ByteReader -}) (batch, uint64, error) { - // Read batch header byte. - b, err := r.ReadByte() - if err != nil { - return batch{}, 0, err - } - if typ := event.Type(b); typ != go122.EvEventBatch && typ != go122.EvExperimentalBatch { - return batch{}, 0, fmt.Errorf("expected batch event, got %s", go122.EventString(typ)) - } - - // Read the experiment of we have one. - exp := event.NoExperiment - if event.Type(b) == go122.EvExperimentalBatch { - e, err := r.ReadByte() - if err != nil { - return batch{}, 0, err - } - exp = event.Experiment(e) - } - - // Read the batch header: gen (generation), thread (M) ID, base timestamp - // for the batch. - gen, err := binary.ReadUvarint(r) - if err != nil { - return batch{}, gen, fmt.Errorf("error reading batch gen: %w", err) - } - m, err := binary.ReadUvarint(r) - if err != nil { - return batch{}, gen, fmt.Errorf("error reading batch M ID: %w", err) - } - ts, err := binary.ReadUvarint(r) - if err != nil { - return batch{}, gen, fmt.Errorf("error reading batch timestamp: %w", err) - } - - // Read in the size of the batch to follow. - size, err := binary.ReadUvarint(r) - if err != nil { - return batch{}, gen, fmt.Errorf("error reading batch size: %w", err) - } - if size > go122.MaxBatchSize { - return batch{}, gen, fmt.Errorf("invalid batch size %d, maximum is %d", size, go122.MaxBatchSize) - } - - // Copy out the batch for later processing. - var data bytes.Buffer - data.Grow(int(size)) - n, err := io.CopyN(&data, r, int64(size)) - if n != int64(size) { - return batch{}, gen, fmt.Errorf("failed to read full batch: read %d but wanted %d", n, size) - } - if err != nil { - return batch{}, gen, fmt.Errorf("copying batch data: %w", err) - } - - // Return the batch. - return batch{ - m: ThreadID(m), - time: timestamp(ts), - data: data.Bytes(), - exp: exp, - }, gen, nil -} diff --git a/src/internal/trace/v2/batchcursor.go b/src/internal/trace/v2/batchcursor.go deleted file mode 100644 index 8dc34fd22f..0000000000 --- a/src/internal/trace/v2/batchcursor.go +++ /dev/null @@ -1,174 +0,0 @@ -// 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. - -package trace - -import ( - "cmp" - "encoding/binary" - "fmt" - - "internal/trace/v2/event" - "internal/trace/v2/event/go122" -) - -type batchCursor struct { - m ThreadID - lastTs Time - idx int // next index into []batch - dataOff int // next index into batch.data - ev baseEvent // last read event -} - -func (b *batchCursor) nextEvent(batches []batch, freq frequency) (ok bool, err error) { - // Batches should generally always have at least one event, - // but let's be defensive about that and accept empty batches. - for b.idx < len(batches) && len(batches[b.idx].data) == b.dataOff { - b.idx++ - b.dataOff = 0 - b.lastTs = 0 - } - // Have we reached the end of the batches? - if b.idx == len(batches) { - return false, nil - } - // Initialize lastTs if it hasn't been yet. - if b.lastTs == 0 { - b.lastTs = freq.mul(batches[b.idx].time) - } - // Read an event out. - n, tsdiff, err := readTimedBaseEvent(batches[b.idx].data[b.dataOff:], &b.ev) - if err != nil { - return false, err - } - // Complete the timestamp from the cursor's last timestamp. - b.ev.time = freq.mul(tsdiff) + b.lastTs - - // Move the cursor's timestamp forward. - b.lastTs = b.ev.time - - // Move the cursor forward. - b.dataOff += n - return true, nil -} - -func (b *batchCursor) compare(a *batchCursor) int { - return cmp.Compare(b.ev.time, a.ev.time) -} - -// readTimedBaseEvent reads out the raw event data from b -// into e. It does not try to interpret the arguments -// but it does validate that the event is a regular -// event with a timestamp (vs. a structural event). -// -// It requires that the event its reading be timed, which must -// be the case for every event in a plain EventBatch. -func readTimedBaseEvent(b []byte, e *baseEvent) (int, timestamp, error) { - // Get the event type. - typ := event.Type(b[0]) - specs := go122.Specs() - if int(typ) >= len(specs) { - return 0, 0, fmt.Errorf("found invalid event type: %v", typ) - } - e.typ = typ - - // Get spec. - spec := &specs[typ] - if len(spec.Args) == 0 || !spec.IsTimedEvent { - return 0, 0, fmt.Errorf("found event without a timestamp: type=%v", typ) - } - n := 1 - - // Read timestamp diff. - ts, nb := binary.Uvarint(b[n:]) - if nb <= 0 { - return 0, 0, fmt.Errorf("found invalid uvarint for timestamp") - } - n += nb - - // Read the rest of the arguments. - for i := 0; i < len(spec.Args)-1; i++ { - arg, nb := binary.Uvarint(b[n:]) - if nb <= 0 { - return 0, 0, fmt.Errorf("found invalid uvarint") - } - e.args[i] = arg - n += nb - } - return n, timestamp(ts), nil -} - -func heapInsert(heap []*batchCursor, bc *batchCursor) []*batchCursor { - // Add the cursor to the end of the heap. - heap = append(heap, bc) - - // Sift the new entry up to the right place. - heapSiftUp(heap, len(heap)-1) - return heap -} - -func heapUpdate(heap []*batchCursor, i int) { - // Try to sift up. - if heapSiftUp(heap, i) != i { - return - } - // Try to sift down, if sifting up failed. - heapSiftDown(heap, i) -} - -func heapRemove(heap []*batchCursor, i int) []*batchCursor { - // Sift index i up to the root, ignoring actual values. - for i > 0 { - heap[(i-1)/2], heap[i] = heap[i], heap[(i-1)/2] - i = (i - 1) / 2 - } - // Swap the root with the last element, then remove it. - heap[0], heap[len(heap)-1] = heap[len(heap)-1], heap[0] - heap = heap[:len(heap)-1] - // Sift the root down. - heapSiftDown(heap, 0) - return heap -} - -func heapSiftUp(heap []*batchCursor, i int) int { - for i > 0 && heap[(i-1)/2].ev.time > heap[i].ev.time { - heap[(i-1)/2], heap[i] = heap[i], heap[(i-1)/2] - i = (i - 1) / 2 - } - return i -} - -func heapSiftDown(heap []*batchCursor, i int) int { - for { - m := min3(heap, i, 2*i+1, 2*i+2) - if m == i { - // Heap invariant already applies. - break - } - heap[i], heap[m] = heap[m], heap[i] - i = m - } - return i -} - -func min3(b []*batchCursor, i0, i1, i2 int) int { - minIdx := i0 - minT := maxTime - if i0 < len(b) { - minT = b[i0].ev.time - } - if i1 < len(b) { - if t := b[i1].ev.time; t < minT { - minT = t - minIdx = i1 - } - } - if i2 < len(b) { - if t := b[i2].ev.time; t < minT { - minT = t - minIdx = i2 - } - } - return minIdx -} diff --git a/src/internal/trace/v2/batchcursor_test.go b/src/internal/trace/v2/batchcursor_test.go deleted file mode 100644 index 69731e5254..0000000000 --- a/src/internal/trace/v2/batchcursor_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// 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. - -package trace - -import ( - "fmt" - "strings" - "testing" - - "slices" -) - -func TestHeap(t *testing.T) { - var heap []*batchCursor - - // Insert a bunch of values into the heap. - checkHeap(t, heap) - heap = heapInsert(heap, makeBatchCursor(5)) - checkHeap(t, heap) - for i := int64(-20); i < 20; i++ { - heap = heapInsert(heap, makeBatchCursor(i)) - checkHeap(t, heap) - } - - // Update an element in the middle to be the new minimum. - for i := range heap { - if heap[i].ev.time == 5 { - heap[i].ev.time = -21 - heapUpdate(heap, i) - break - } - } - checkHeap(t, heap) - if heap[0].ev.time != -21 { - t.Fatalf("heap update failed, expected %d as heap min: %s", -21, heapDebugString(heap)) - } - - // Update the minimum element to be smaller. There should be no change. - heap[0].ev.time = -22 - heapUpdate(heap, 0) - checkHeap(t, heap) - if heap[0].ev.time != -22 { - t.Fatalf("heap update failed, expected %d as heap min: %s", -22, heapDebugString(heap)) - } - - // Update the last element to be larger. There should be no change. - heap[len(heap)-1].ev.time = 21 - heapUpdate(heap, len(heap)-1) - checkHeap(t, heap) - if heap[len(heap)-1].ev.time != 21 { - t.Fatalf("heap update failed, expected %d as heap min: %s", 21, heapDebugString(heap)) - } - - // Update the last element to be smaller. - heap[len(heap)-1].ev.time = 7 - heapUpdate(heap, len(heap)-1) - checkHeap(t, heap) - if heap[len(heap)-1].ev.time == 21 { - t.Fatalf("heap update failed, unexpected %d as heap min: %s", 21, heapDebugString(heap)) - } - - // Remove an element in the middle. - for i := range heap { - if heap[i].ev.time == 5 { - heap = heapRemove(heap, i) - break - } - } - checkHeap(t, heap) - for i := range heap { - if heap[i].ev.time == 5 { - t.Fatalf("failed to remove heap elem with time %d: %s", 5, heapDebugString(heap)) - } - } - - // Remove tail. - heap = heapRemove(heap, len(heap)-1) - checkHeap(t, heap) - - // Remove from the head, and make sure the result is sorted. - l := len(heap) - var removed []*batchCursor - for i := 0; i < l; i++ { - removed = append(removed, heap[0]) - heap = heapRemove(heap, 0) - checkHeap(t, heap) - } - if !slices.IsSortedFunc(removed, (*batchCursor).compare) { - t.Fatalf("heap elements not removed in sorted order, got: %s", heapDebugString(removed)) - } -} - -func makeBatchCursor(v int64) *batchCursor { - return &batchCursor{ev: baseEvent{time: Time(v)}} -} - -func heapDebugString(heap []*batchCursor) string { - var sb strings.Builder - fmt.Fprintf(&sb, "[") - for i := range heap { - if i != 0 { - fmt.Fprintf(&sb, ", ") - } - fmt.Fprintf(&sb, "%d", heap[i].ev.time) - } - fmt.Fprintf(&sb, "]") - return sb.String() -} - -func checkHeap(t *testing.T, heap []*batchCursor) { - t.Helper() - - for i := range heap { - if i == 0 { - continue - } - if heap[(i-1)/2].compare(heap[i]) > 0 { - t.Errorf("heap invariant not maintained between index %d and parent %d: %s", i, i/2, heapDebugString(heap)) - } - } - if t.Failed() { - t.FailNow() - } -} diff --git a/src/internal/trace/v2/event.go b/src/internal/trace/v2/event.go deleted file mode 100644 index c58d3bcba2..0000000000 --- a/src/internal/trace/v2/event.go +++ /dev/null @@ -1,863 +0,0 @@ -// 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. - -package trace - -import ( - "fmt" - "math" - "strings" - "time" - - "internal/trace/v2/event" - "internal/trace/v2/event/go122" - "internal/trace/v2/version" -) - -// EventKind indicates the kind of event this is. -// -// Use this information to obtain a more specific event that -// allows access to more detailed information. -type EventKind uint16 - -const ( - EventBad EventKind = iota - - // EventKindSync is an event that indicates a global synchronization - // point in the trace. At the point of a sync event, the - // trace reader can be certain that all resources (e.g. threads, - // goroutines) that have existed until that point have been enumerated. - EventSync - - // EventMetric is an event that represents the value of a metric at - // a particular point in time. - EventMetric - - // EventLabel attaches a label to a resource. - EventLabel - - // EventStackSample represents an execution sample, indicating what a - // thread/proc/goroutine was doing at a particular point in time via - // its backtrace. - // - // Note: Samples should be considered a close approximation of - // what a thread/proc/goroutine was executing at a given point in time. - // These events may slightly contradict the situation StateTransitions - // describe, so they should only be treated as a best-effort annotation. - EventStackSample - - // EventRangeBegin and EventRangeEnd are a pair of generic events representing - // a special range of time. Ranges are named and scoped to some resource - // (identified via ResourceKind). A range that has begun but has not ended - // is considered active. - // - // EvRangeBegin and EvRangeEnd will share the same name, and an End will always - // follow a Begin on the same instance of the resource. The associated - // resource ID can be obtained from the Event. ResourceNone indicates the - // range is globally scoped. That is, any goroutine/proc/thread can start or - // stop, but only one such range may be active at any given time. - // - // EventRangeActive is like EventRangeBegin, but indicates that the range was - // already active. In this case, the resource referenced may not be in the current - // context. - EventRangeBegin - EventRangeActive - EventRangeEnd - - // EvTaskBegin and EvTaskEnd are a pair of events representing a runtime/trace.Task. - EventTaskBegin - EventTaskEnd - - // EventRegionBegin and EventRegionEnd are a pair of events represent a runtime/trace.Region. - EventRegionBegin - EventRegionEnd - - // EventLog represents a runtime/trace.Log call. - EventLog - - // EventStateTransition represents a state change for some resource. - EventStateTransition - - // EventExperimental is an experimental event that is unvalidated and exposed in a raw form. - // Users are expected to understand the format and perform their own validation. These events - // may always be safely ignored. - EventExperimental -) - -// String returns a string form of the EventKind. -func (e EventKind) String() string { - if int(e) >= len(eventKindStrings) { - return eventKindStrings[0] - } - return eventKindStrings[e] -} - -var eventKindStrings = [...]string{ - EventBad: "Bad", - EventSync: "Sync", - EventMetric: "Metric", - EventLabel: "Label", - EventStackSample: "StackSample", - EventRangeBegin: "RangeBegin", - EventRangeActive: "RangeActive", - EventRangeEnd: "RangeEnd", - EventTaskBegin: "TaskBegin", - EventTaskEnd: "TaskEnd", - EventRegionBegin: "RegionBegin", - EventRegionEnd: "RegionEnd", - EventLog: "Log", - EventStateTransition: "StateTransition", - EventExperimental: "Experimental", -} - -const maxTime = Time(math.MaxInt64) - -// Time is a timestamp in nanoseconds. -// -// It corresponds to the monotonic clock on the platform that the -// trace was taken, and so is possible to correlate with timestamps -// for other traces taken on the same machine using the same clock -// (i.e. no reboots in between). -// -// The actual absolute value of the timestamp is only meaningful in -// relation to other timestamps from the same clock. -// -// BUG: Timestamps coming from traces on Windows platforms are -// only comparable with timestamps from the same trace. Timestamps -// across traces cannot be compared, because the system clock is -// not used as of Go 1.22. -// -// BUG: Traces produced by Go versions 1.21 and earlier cannot be -// compared with timestamps from other traces taken on the same -// machine. This is because the system clock was not used at all -// to collect those timestamps. -type Time int64 - -// Sub subtracts t0 from t, returning the duration in nanoseconds. -func (t Time) Sub(t0 Time) time.Duration { - return time.Duration(int64(t) - int64(t0)) -} - -// Metric provides details about a Metric event. -type Metric struct { - // Name is the name of the sampled metric. - // - // Names follow the same convention as metric names in the - // runtime/metrics package, meaning they include the unit. - // Names that match with the runtime/metrics package represent - // the same quantity. Note that this corresponds to the - // runtime/metrics package for the Go version this trace was - // collected for. - Name string - - // Value is the sampled value of the metric. - // - // The Value's Kind is tied to the name of the metric, and so is - // guaranteed to be the same for metric samples for the same metric. - Value Value -} - -// Label provides details about a Label event. -type Label struct { - // Label is the label applied to some resource. - Label string - - // Resource is the resource to which this label should be applied. - Resource ResourceID -} - -// Range provides details about a Range event. -type Range struct { - // Name is a human-readable name for the range. - // - // This name can be used to identify the end of the range for the resource - // its scoped to, because only one of each type of range may be active on - // a particular resource. The relevant resource should be obtained from the - // Event that produced these details. The corresponding RangeEnd will have - // an identical name. - Name string - - // Scope is the resource that the range is scoped to. - // - // For example, a ResourceGoroutine scope means that the same goroutine - // must have a start and end for the range, and that goroutine can only - // have one range of a particular name active at any given time. The - // ID that this range is scoped to may be obtained via Event.Goroutine. - // - // The ResourceNone scope means that the range is globally scoped. As a - // result, any goroutine/proc/thread may start or end the range, and only - // one such named range may be active globally at any given time. - // - // For RangeBegin and RangeEnd events, this will always reference some - // resource ID in the current execution context. For RangeActive events, - // this may reference a resource not in the current context. Prefer Scope - // over the current execution context. - Scope ResourceID -} - -// RangeAttributes provides attributes about a completed Range. -type RangeAttribute struct { - // Name is the human-readable name for the range. - Name string - - // Value is the value of the attribute. - Value Value -} - -// TaskID is the internal ID of a task used to disambiguate tasks (even if they -// are of the same type). -type TaskID uint64 - -const ( - // NoTask indicates the lack of a task. - NoTask = TaskID(^uint64(0)) - - // BackgroundTask is the global task that events are attached to if there was - // no other task in the context at the point the event was emitted. - BackgroundTask = TaskID(0) -) - -// Task provides details about a Task event. -type Task struct { - // ID is a unique identifier for the task. - // - // This can be used to associate the beginning of a task with its end. - ID TaskID - - // ParentID is the ID of the parent task. - Parent TaskID - - // Type is the taskType that was passed to runtime/trace.NewTask. - // - // May be "" if a task's TaskBegin event isn't present in the trace. - Type string -} - -// Region provides details about a Region event. -type Region struct { - // Task is the ID of the task this region is associated with. - Task TaskID - - // Type is the regionType that was passed to runtime/trace.StartRegion or runtime/trace.WithRegion. - Type string -} - -// Log provides details about a Log event. -type Log struct { - // Task is the ID of the task this region is associated with. - Task TaskID - - // Category is the category that was passed to runtime/trace.Log or runtime/trace.Logf. - Category string - - // Message is the message that was passed to runtime/trace.Log or runtime/trace.Logf. - Message string -} - -// Stack represents a stack. It's really a handle to a stack and it's trivially comparable. -// -// If two Stacks are equal then their Frames are guaranteed to be identical. If they are not -// equal, however, their Frames may still be equal. -type Stack struct { - table *evTable - id stackID -} - -// Frames is an iterator over the frames in a Stack. -func (s Stack) Frames(yield func(f StackFrame) bool) bool { - if s.id == 0 { - return true - } - stk := s.table.stacks.mustGet(s.id) - for _, pc := range stk.pcs { - f := s.table.pcs[pc] - sf := StackFrame{ - PC: f.pc, - Func: s.table.strings.mustGet(f.funcID), - File: s.table.strings.mustGet(f.fileID), - Line: f.line, - } - if !yield(sf) { - return false - } - } - return true -} - -// NoStack is a sentinel value that can be compared against any Stack value, indicating -// a lack of a stack trace. -var NoStack = Stack{} - -// StackFrame represents a single frame of a stack. -type StackFrame struct { - // PC is the program counter of the function call if this - // is not a leaf frame. If it's a leaf frame, it's the point - // at which the stack trace was taken. - PC uint64 - - // Func is the name of the function this frame maps to. - Func string - - // File is the file which contains the source code of Func. - File string - - // Line is the line number within File which maps to PC. - Line uint64 -} - -// ExperimentalEvent presents a raw view of an experimental event's arguments and thier names. -type ExperimentalEvent struct { - // Name is the name of the event. - Name string - - // ArgNames is the names of the event's arguments in order. - // This may refer to a globally shared slice. Copy before mutating. - ArgNames []string - - // Args contains the event's arguments. - Args []uint64 - - // Data is additional unparsed data that is associated with the experimental event. - // Data is likely to be shared across many ExperimentalEvents, so callers that parse - // Data are encouraged to cache the parse result and look it up by the value of Data. - Data *ExperimentalData -} - -// ExperimentalData represents some raw and unparsed sidecar data present in the trace that is -// associated with certain kinds of experimental events. For example, this data may contain -// tables needed to interpret ExperimentalEvent arguments, or the ExperimentEvent could just be -// a placeholder for a differently encoded event that's actually present in the experimental data. -type ExperimentalData struct { - // Batches contain the actual experimental data, along with metadata about each batch. - Batches []ExperimentalBatch -} - -// ExperimentalBatch represents a packet of unparsed data along with metadata about that packet. -type ExperimentalBatch struct { - // Thread is the ID of the thread that produced a packet of data. - Thread ThreadID - - // Data is a packet of unparsed data all produced by one thread. - Data []byte -} - -// Event represents a single event in the trace. -type Event struct { - table *evTable - ctx schedCtx - base baseEvent -} - -// Kind returns the kind of event that this is. -func (e Event) Kind() EventKind { - return go122Type2Kind[e.base.typ] -} - -// Time returns the timestamp of the event. -func (e Event) Time() Time { - return e.base.time -} - -// Goroutine returns the ID of the goroutine that was executing when -// this event happened. It describes part of the execution context -// for this event. -// -// Note that for goroutine state transitions this always refers to the -// state before the transition. For example, if a goroutine is just -// starting to run on this thread and/or proc, then this will return -// NoGoroutine. In this case, the goroutine starting to run will be -// can be found at Event.StateTransition().Resource. -func (e Event) Goroutine() GoID { - return e.ctx.G -} - -// Proc returns the ID of the proc this event event pertains to. -// -// Note that for proc state transitions this always refers to the -// state before the transition. For example, if a proc is just -// starting to run on this thread, then this will return NoProc. -func (e Event) Proc() ProcID { - return e.ctx.P -} - -// Thread returns the ID of the thread this event pertains to. -// -// Note that for thread state transitions this always refers to the -// state before the transition. For example, if a thread is just -// starting to run, then this will return NoThread. -// -// Note: tracking thread state is not currently supported, so this -// will always return a valid thread ID. However thread state transitions -// may be tracked in the future, and callers must be robust to this -// possibility. -func (e Event) Thread() ThreadID { - return e.ctx.M -} - -// Stack returns a handle to a stack associated with the event. -// -// This represents a stack trace at the current moment in time for -// the current execution context. -func (e Event) Stack() Stack { - if e.base.typ == evSync { - return NoStack - } - if e.base.typ == go122.EvCPUSample { - return Stack{table: e.table, id: stackID(e.base.args[0])} - } - spec := go122.Specs()[e.base.typ] - if len(spec.StackIDs) == 0 { - return NoStack - } - // The stack for the main execution context is always the - // first stack listed in StackIDs. Subtract one from this - // because we've peeled away the timestamp argument. - id := stackID(e.base.args[spec.StackIDs[0]-1]) - if id == 0 { - return NoStack - } - return Stack{table: e.table, id: id} -} - -// Metric returns details about a Metric event. -// -// Panics if Kind != EventMetric. -func (e Event) Metric() Metric { - if e.Kind() != EventMetric { - panic("Metric called on non-Metric event") - } - var m Metric - switch e.base.typ { - case go122.EvProcsChange: - m.Name = "/sched/gomaxprocs:threads" - m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} - case go122.EvHeapAlloc: - m.Name = "/memory/classes/heap/objects:bytes" - m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} - case go122.EvHeapGoal: - m.Name = "/gc/heap/goal:bytes" - m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} - default: - panic(fmt.Sprintf("internal error: unexpected event type for Metric kind: %s", go122.EventString(e.base.typ))) - } - return m -} - -// Label returns details about a Label event. -// -// Panics if Kind != EventLabel. -func (e Event) Label() Label { - if e.Kind() != EventLabel { - panic("Label called on non-Label event") - } - if e.base.typ != go122.EvGoLabel { - panic(fmt.Sprintf("internal error: unexpected event type for Label kind: %s", go122.EventString(e.base.typ))) - } - return Label{ - Label: e.table.strings.mustGet(stringID(e.base.args[0])), - Resource: ResourceID{Kind: ResourceGoroutine, id: int64(e.ctx.G)}, - } -} - -// Range returns details about an EventRangeBegin, EventRangeActive, or EventRangeEnd event. -// -// Panics if Kind != EventRangeBegin, Kind != EventRangeActive, and Kind != EventRangeEnd. -func (e Event) Range() Range { - if kind := e.Kind(); kind != EventRangeBegin && kind != EventRangeActive && kind != EventRangeEnd { - panic("Range called on non-Range event") - } - var r Range - switch e.base.typ { - case go122.EvSTWBegin, go122.EvSTWEnd: - // N.B. ordering.advance smuggles in the STW reason as e.base.args[0] - // for go122.EvSTWEnd (it's already there for Begin). - r.Name = "stop-the-world (" + e.table.strings.mustGet(stringID(e.base.args[0])) + ")" - r.Scope = ResourceID{Kind: ResourceGoroutine, id: int64(e.Goroutine())} - case go122.EvGCBegin, go122.EvGCActive, go122.EvGCEnd: - r.Name = "GC concurrent mark phase" - r.Scope = ResourceID{Kind: ResourceNone} - case go122.EvGCSweepBegin, go122.EvGCSweepActive, go122.EvGCSweepEnd: - r.Name = "GC incremental sweep" - r.Scope = ResourceID{Kind: ResourceProc} - if e.base.typ == go122.EvGCSweepActive { - r.Scope.id = int64(e.base.args[0]) - } else { - r.Scope.id = int64(e.Proc()) - } - r.Scope.id = int64(e.Proc()) - case go122.EvGCMarkAssistBegin, go122.EvGCMarkAssistActive, go122.EvGCMarkAssistEnd: - r.Name = "GC mark assist" - r.Scope = ResourceID{Kind: ResourceGoroutine} - if e.base.typ == go122.EvGCMarkAssistActive { - r.Scope.id = int64(e.base.args[0]) - } else { - r.Scope.id = int64(e.Goroutine()) - } - default: - panic(fmt.Sprintf("internal error: unexpected event type for Range kind: %s", go122.EventString(e.base.typ))) - } - return r -} - -// RangeAttributes returns attributes for a completed range. -// -// Panics if Kind != EventRangeEnd. -func (e Event) RangeAttributes() []RangeAttribute { - if e.Kind() != EventRangeEnd { - panic("Range called on non-Range event") - } - if e.base.typ != go122.EvGCSweepEnd { - return nil - } - return []RangeAttribute{ - { - Name: "bytes swept", - Value: Value{kind: ValueUint64, scalar: e.base.args[0]}, - }, - { - Name: "bytes reclaimed", - Value: Value{kind: ValueUint64, scalar: e.base.args[1]}, - }, - } -} - -// Task returns details about a TaskBegin or TaskEnd event. -// -// Panics if Kind != EventTaskBegin and Kind != EventTaskEnd. -func (e Event) Task() Task { - if kind := e.Kind(); kind != EventTaskBegin && kind != EventTaskEnd { - panic("Task called on non-Task event") - } - parentID := NoTask - var typ string - switch e.base.typ { - case go122.EvUserTaskBegin: - parentID = TaskID(e.base.args[1]) - typ = e.table.strings.mustGet(stringID(e.base.args[2])) - case go122.EvUserTaskEnd: - parentID = TaskID(e.base.extra(version.Go122)[0]) - typ = e.table.getExtraString(extraStringID(e.base.extra(version.Go122)[1])) - default: - panic(fmt.Sprintf("internal error: unexpected event type for Task kind: %s", go122.EventString(e.base.typ))) - } - return Task{ - ID: TaskID(e.base.args[0]), - Parent: parentID, - Type: typ, - } -} - -// Region returns details about a RegionBegin or RegionEnd event. -// -// Panics if Kind != EventRegionBegin and Kind != EventRegionEnd. -func (e Event) Region() Region { - if kind := e.Kind(); kind != EventRegionBegin && kind != EventRegionEnd { - panic("Region called on non-Region event") - } - if e.base.typ != go122.EvUserRegionBegin && e.base.typ != go122.EvUserRegionEnd { - panic(fmt.Sprintf("internal error: unexpected event type for Region kind: %s", go122.EventString(e.base.typ))) - } - return Region{ - Task: TaskID(e.base.args[0]), - Type: e.table.strings.mustGet(stringID(e.base.args[1])), - } -} - -// Log returns details about a Log event. -// -// Panics if Kind != EventLog. -func (e Event) Log() Log { - if e.Kind() != EventLog { - panic("Log called on non-Log event") - } - if e.base.typ != go122.EvUserLog { - panic(fmt.Sprintf("internal error: unexpected event type for Log kind: %s", go122.EventString(e.base.typ))) - } - return Log{ - Task: TaskID(e.base.args[0]), - Category: e.table.strings.mustGet(stringID(e.base.args[1])), - Message: e.table.strings.mustGet(stringID(e.base.args[2])), - } -} - -// StateTransition returns details about a StateTransition event. -// -// Panics if Kind != EventStateTransition. -func (e Event) StateTransition() StateTransition { - if e.Kind() != EventStateTransition { - panic("StateTransition called on non-StateTransition event") - } - var s StateTransition - switch e.base.typ { - case go122.EvProcStart: - s = procStateTransition(ProcID(e.base.args[0]), ProcIdle, ProcRunning) - case go122.EvProcStop: - s = procStateTransition(e.ctx.P, ProcRunning, ProcIdle) - case go122.EvProcSteal: - // N.B. ordering.advance populates e.base.extra. - beforeState := ProcRunning - if go122.ProcStatus(e.base.extra(version.Go122)[0]) == go122.ProcSyscallAbandoned { - // We've lost information because this ProcSteal advanced on a - // SyscallAbandoned state. Treat the P as idle because ProcStatus - // treats SyscallAbandoned as Idle. Otherwise we'll have an invalid - // transition. - beforeState = ProcIdle - } - s = procStateTransition(ProcID(e.base.args[0]), beforeState, ProcIdle) - case go122.EvProcStatus: - // N.B. ordering.advance populates e.base.extra. - s = procStateTransition(ProcID(e.base.args[0]), ProcState(e.base.extra(version.Go122)[0]), go122ProcStatus2ProcState[e.base.args[1]]) - case go122.EvGoCreate, go122.EvGoCreateBlocked: - status := GoRunnable - if e.base.typ == go122.EvGoCreateBlocked { - status = GoWaiting - } - s = goStateTransition(GoID(e.base.args[0]), GoNotExist, status) - s.Stack = Stack{table: e.table, id: stackID(e.base.args[1])} - case go122.EvGoCreateSyscall: - s = goStateTransition(GoID(e.base.args[0]), GoNotExist, GoSyscall) - case go122.EvGoStart: - s = goStateTransition(GoID(e.base.args[0]), GoRunnable, GoRunning) - case go122.EvGoDestroy: - s = goStateTransition(e.ctx.G, GoRunning, GoNotExist) - s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoDestroySyscall: - s = goStateTransition(e.ctx.G, GoSyscall, GoNotExist) - case go122.EvGoStop: - s = goStateTransition(e.ctx.G, GoRunning, GoRunnable) - s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) - s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoBlock: - s = goStateTransition(e.ctx.G, GoRunning, GoWaiting) - s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) - s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoUnblock, go122.EvGoSwitch, go122.EvGoSwitchDestroy: - // N.B. GoSwitch and GoSwitchDestroy both emit additional events, but - // the first thing they both do is unblock the goroutine they name, - // identically to an unblock event (even their arguments match). - s = goStateTransition(GoID(e.base.args[0]), GoWaiting, GoRunnable) - case go122.EvGoSyscallBegin: - s = goStateTransition(e.ctx.G, GoRunning, GoSyscall) - s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoSyscallEnd: - s = goStateTransition(e.ctx.G, GoSyscall, GoRunning) - s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoSyscallEndBlocked: - s = goStateTransition(e.ctx.G, GoSyscall, GoRunnable) - s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoStatus, go122.EvGoStatusStack: - // N.B. ordering.advance populates e.base.extra. - s = goStateTransition(GoID(e.base.args[0]), GoState(e.base.extra(version.Go122)[0]), go122GoStatus2GoState[e.base.args[2]]) - default: - panic(fmt.Sprintf("internal error: unexpected event type for StateTransition kind: %s", go122.EventString(e.base.typ))) - } - return s -} - -// Experimental returns a view of the raw event for an experimental event. -// -// Panics if Kind != EventExperimental. -func (e Event) Experimental() ExperimentalEvent { - if e.Kind() != EventExperimental { - panic("Experimental called on non-Experimental event") - } - spec := go122.Specs()[e.base.typ] - argNames := spec.Args[1:] - return ExperimentalEvent{ - Name: spec.Name, - ArgNames: argNames, // Skip timestamp; already handled. - Args: e.base.args[1 : 1+len(argNames)], - Data: e.table.expData[spec.Experiment], - } -} - -const evSync = ^event.Type(0) - -var go122Type2Kind = [...]EventKind{ - go122.EvCPUSample: EventStackSample, - go122.EvProcsChange: EventMetric, - go122.EvProcStart: EventStateTransition, - go122.EvProcStop: EventStateTransition, - go122.EvProcSteal: EventStateTransition, - go122.EvProcStatus: EventStateTransition, - go122.EvGoCreate: EventStateTransition, - go122.EvGoCreateSyscall: EventStateTransition, - go122.EvGoStart: EventStateTransition, - go122.EvGoDestroy: EventStateTransition, - go122.EvGoDestroySyscall: EventStateTransition, - go122.EvGoStop: EventStateTransition, - go122.EvGoBlock: EventStateTransition, - go122.EvGoUnblock: EventStateTransition, - go122.EvGoSyscallBegin: EventStateTransition, - go122.EvGoSyscallEnd: EventStateTransition, - go122.EvGoSyscallEndBlocked: EventStateTransition, - go122.EvGoStatus: EventStateTransition, - go122.EvSTWBegin: EventRangeBegin, - go122.EvSTWEnd: EventRangeEnd, - go122.EvGCActive: EventRangeActive, - go122.EvGCBegin: EventRangeBegin, - go122.EvGCEnd: EventRangeEnd, - go122.EvGCSweepActive: EventRangeActive, - go122.EvGCSweepBegin: EventRangeBegin, - go122.EvGCSweepEnd: EventRangeEnd, - go122.EvGCMarkAssistActive: EventRangeActive, - go122.EvGCMarkAssistBegin: EventRangeBegin, - go122.EvGCMarkAssistEnd: EventRangeEnd, - go122.EvHeapAlloc: EventMetric, - go122.EvHeapGoal: EventMetric, - go122.EvGoLabel: EventLabel, - go122.EvUserTaskBegin: EventTaskBegin, - go122.EvUserTaskEnd: EventTaskEnd, - go122.EvUserRegionBegin: EventRegionBegin, - go122.EvUserRegionEnd: EventRegionEnd, - go122.EvUserLog: EventLog, - go122.EvGoSwitch: EventStateTransition, - go122.EvGoSwitchDestroy: EventStateTransition, - go122.EvGoCreateBlocked: EventStateTransition, - go122.EvGoStatusStack: EventStateTransition, - go122.EvSpan: EventExperimental, - go122.EvSpanAlloc: EventExperimental, - go122.EvSpanFree: EventExperimental, - go122.EvHeapObject: EventExperimental, - go122.EvHeapObjectAlloc: EventExperimental, - go122.EvHeapObjectFree: EventExperimental, - go122.EvGoroutineStack: EventExperimental, - go122.EvGoroutineStackAlloc: EventExperimental, - go122.EvGoroutineStackFree: EventExperimental, - evSync: EventSync, -} - -var go122GoStatus2GoState = [...]GoState{ - go122.GoRunnable: GoRunnable, - go122.GoRunning: GoRunning, - go122.GoWaiting: GoWaiting, - go122.GoSyscall: GoSyscall, -} - -var go122ProcStatus2ProcState = [...]ProcState{ - go122.ProcRunning: ProcRunning, - go122.ProcIdle: ProcIdle, - go122.ProcSyscall: ProcRunning, - go122.ProcSyscallAbandoned: ProcIdle, -} - -// String returns the event as a human-readable string. -// -// The format of the string is intended for debugging and is subject to change. -func (e Event) String() string { - var sb strings.Builder - fmt.Fprintf(&sb, "M=%d P=%d G=%d", e.Thread(), e.Proc(), e.Goroutine()) - fmt.Fprintf(&sb, " %s Time=%d", e.Kind(), e.Time()) - // Kind-specific fields. - switch kind := e.Kind(); kind { - case EventMetric: - m := e.Metric() - fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, valueAsString(m.Value)) - case EventLabel: - l := e.Label() - fmt.Fprintf(&sb, " Label=%q Resource=%s", l.Label, l.Resource) - case EventRangeBegin, EventRangeActive, EventRangeEnd: - r := e.Range() - fmt.Fprintf(&sb, " Name=%q Scope=%s", r.Name, r.Scope) - if kind == EventRangeEnd { - fmt.Fprintf(&sb, " Attributes=[") - for i, attr := range e.RangeAttributes() { - if i != 0 { - fmt.Fprintf(&sb, " ") - } - fmt.Fprintf(&sb, "%q=%s", attr.Name, valueAsString(attr.Value)) - } - fmt.Fprintf(&sb, "]") - } - case EventTaskBegin, EventTaskEnd: - t := e.Task() - fmt.Fprintf(&sb, " ID=%d Parent=%d Type=%q", t.ID, t.Parent, t.Type) - case EventRegionBegin, EventRegionEnd: - r := e.Region() - fmt.Fprintf(&sb, " Task=%d Type=%q", r.Task, r.Type) - case EventLog: - l := e.Log() - fmt.Fprintf(&sb, " Task=%d Category=%q Message=%q", l.Task, l.Category, l.Message) - case EventStateTransition: - s := e.StateTransition() - fmt.Fprintf(&sb, " Resource=%s Reason=%q", s.Resource, s.Reason) - switch s.Resource.Kind { - case ResourceGoroutine: - id := s.Resource.Goroutine() - old, new := s.Goroutine() - fmt.Fprintf(&sb, " GoID=%d %s->%s", id, old, new) - case ResourceProc: - id := s.Resource.Proc() - old, new := s.Proc() - fmt.Fprintf(&sb, " ProcID=%d %s->%s", id, old, new) - } - if s.Stack != NoStack { - fmt.Fprintln(&sb) - fmt.Fprintln(&sb, "TransitionStack=") - s.Stack.Frames(func(f StackFrame) bool { - fmt.Fprintf(&sb, "\t%s @ 0x%x\n", f.Func, f.PC) - fmt.Fprintf(&sb, "\t\t%s:%d\n", f.File, f.Line) - return true - }) - } - case EventExperimental: - r := e.Experimental() - fmt.Fprintf(&sb, " Name=%s ArgNames=%v Args=%v", r.Name, r.ArgNames, r.Args) - } - if stk := e.Stack(); stk != NoStack { - fmt.Fprintln(&sb) - fmt.Fprintln(&sb, "Stack=") - stk.Frames(func(f StackFrame) bool { - fmt.Fprintf(&sb, "\t%s @ 0x%x\n", f.Func, f.PC) - fmt.Fprintf(&sb, "\t\t%s:%d\n", f.File, f.Line) - return true - }) - } - return sb.String() -} - -// validateTableIDs checks to make sure lookups in e.table -// will work. -func (e Event) validateTableIDs() error { - if e.base.typ == evSync { - return nil - } - spec := go122.Specs()[e.base.typ] - - // Check stacks. - for _, i := range spec.StackIDs { - id := stackID(e.base.args[i-1]) - _, ok := e.table.stacks.get(id) - if !ok { - return fmt.Errorf("found invalid stack ID %d for event %s", id, spec.Name) - } - } - // N.B. Strings referenced by stack frames are validated - // early on, when reading the stacks in to begin with. - - // Check strings. - for _, i := range spec.StringIDs { - id := stringID(e.base.args[i-1]) - _, ok := e.table.strings.get(id) - if !ok { - return fmt.Errorf("found invalid string ID %d for event %s", id, spec.Name) - } - } - return nil -} - -func syncEvent(table *evTable, ts Time) Event { - return Event{ - table: table, - ctx: schedCtx{ - G: NoGoroutine, - P: NoProc, - M: NoThread, - }, - base: baseEvent{ - typ: evSync, - time: ts, - }, - } -} diff --git a/src/internal/trace/v2/event/event.go b/src/internal/trace/v2/event/event.go deleted file mode 100644 index adcb8811d8..0000000000 --- a/src/internal/trace/v2/event/event.go +++ /dev/null @@ -1,102 +0,0 @@ -// 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. - -package event - -// Type is the common in-memory representation of the low-leve -type Type uint8 - -// Spec is a specification for a trace event. It contains sufficient information -// to perform basic parsing of any trace event for any version of Go. -type Spec struct { - // Name is the human-readable name of the trace event. - Name string - - // Args contains the names of each trace event's argument. - // Its length determines the number of arguments an event has. - // - // Argument names follow a certain structure and this structure - // is relied on by the testing framework to type-check arguments. - // The structure is is: - // - // (?P[A-Za-z]+_)?(?P[A-Za-z]+) - // - // In sum, it's an optional name followed by a type. If the name - // is present, it is separated from the type with an underscore. - // The valid argument types and the Go types they map to are listed - // in the ArgTypes variable. - Args []string - - // StringIDs indicates which of the arguments are string IDs. - StringIDs []int - - // StackIDs indicates which of the arguments are stack IDs. - // - // The list is not sorted. The first index always refers to - // the main stack for the current execution context of the event. - StackIDs []int - - // StartEv indicates the event type of the corresponding "start" - // event, if this event is an "end," for a pair of events that - // represent a time range. - StartEv Type - - // IsTimedEvent indicates whether this is an event that both - // appears in the main event stream and is surfaced to the - // trace reader. - // - // Events that are not "timed" are considered "structural" - // since they either need significant reinterpretation or - // otherwise aren't actually surfaced by the trace reader. - IsTimedEvent bool - - // HasData is true if the event has trailer consisting of a - // varint length followed by unencoded bytes of some data. - // - // An event may not be both a timed event and have data. - HasData bool - - // IsStack indicates that the event represents a complete - // stack trace. Specifically, it means that after the arguments - // there's a varint length, followed by 4*length varints. Each - // group of 4 represents the PC, file ID, func ID, and line number - // in that order. - IsStack bool - - // Experiment indicates the ID of an experiment this event is associated - // with. If Experiment is not NoExperiment, then the event is experimental - // and will be exposed as an EventExperiment. - Experiment Experiment -} - -// ArgTypes is a list of valid argument types for use in Args. -// -// See the documentation of Args for more details. -var ArgTypes = [...]string{ - "seq", // sequence number - "pstatus", // P status - "gstatus", // G status - "g", // trace.GoID - "m", // trace.ThreadID - "p", // trace.ProcID - "string", // string ID - "stack", // stack ID - "value", // uint64 - "task", // trace.TaskID -} - -// Names is a helper that produces a mapping of event names to event types. -func Names(specs []Spec) map[string]Type { - nameToType := make(map[string]Type) - for i, spec := range specs { - nameToType[spec.Name] = Type(byte(i)) - } - return nameToType -} - -// Experiment is an experiment ID that events may be associated with. -type Experiment uint - -// NoExperiment is the reserved ID 0 indicating no experiment. -const NoExperiment Experiment = 0 diff --git a/src/internal/trace/v2/event/go122/event.go b/src/internal/trace/v2/event/go122/event.go deleted file mode 100644 index df25bfc318..0000000000 --- a/src/internal/trace/v2/event/go122/event.go +++ /dev/null @@ -1,511 +0,0 @@ -// 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. - -package go122 - -import ( - "fmt" - "internal/trace/v2/event" -) - -const ( - EvNone event.Type = iota // unused - - // Structural events. - EvEventBatch // start of per-M batch of events [generation, M ID, timestamp, batch length] - EvStacks // start of a section of the stack table [...EvStack] - EvStack // stack table entry [ID, ...{PC, func string ID, file string ID, line #}] - EvStrings // start of a section of the string dictionary [...EvString] - EvString // string dictionary entry [ID, length, string] - EvCPUSamples // start of a section of CPU samples [...EvCPUSample] - EvCPUSample // CPU profiling sample [timestamp, M ID, P ID, goroutine ID, stack ID] - EvFrequency // timestamp units per sec [freq] - - // Procs. - EvProcsChange // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack ID] - EvProcStart // start of P [timestamp, P ID, P seq] - EvProcStop // stop of P [timestamp] - EvProcSteal // P was stolen [timestamp, P ID, P seq, M ID] - EvProcStatus // P status at the start of a generation [timestamp, P ID, status] - - // Goroutines. - EvGoCreate // goroutine creation [timestamp, new goroutine ID, new stack ID, stack ID] - EvGoCreateSyscall // goroutine appears in syscall (cgo callback) [timestamp, new goroutine ID] - EvGoStart // goroutine starts running [timestamp, goroutine ID, goroutine seq] - EvGoDestroy // goroutine ends [timestamp] - EvGoDestroySyscall // goroutine ends in syscall (cgo callback) [timestamp] - EvGoStop // goroutine yields its time, but is runnable [timestamp, reason, stack ID] - EvGoBlock // goroutine blocks [timestamp, reason, stack ID] - EvGoUnblock // goroutine is unblocked [timestamp, goroutine ID, goroutine seq, stack ID] - EvGoSyscallBegin // syscall enter [timestamp, P seq, stack ID] - EvGoSyscallEnd // syscall exit [timestamp] - EvGoSyscallEndBlocked // syscall exit and it blocked at some point [timestamp] - EvGoStatus // goroutine status at the start of a generation [timestamp, goroutine ID, thread ID, status] - - // STW. - EvSTWBegin // STW start [timestamp, kind] - EvSTWEnd // STW done [timestamp] - - // GC events. - EvGCActive // GC active [timestamp, seq] - EvGCBegin // GC start [timestamp, seq, stack ID] - EvGCEnd // GC done [timestamp, seq] - EvGCSweepActive // GC sweep active [timestamp, P ID] - EvGCSweepBegin // GC sweep start [timestamp, stack ID] - EvGCSweepEnd // GC sweep done [timestamp, swept bytes, reclaimed bytes] - EvGCMarkAssistActive // GC mark assist active [timestamp, goroutine ID] - EvGCMarkAssistBegin // GC mark assist start [timestamp, stack ID] - EvGCMarkAssistEnd // GC mark assist done [timestamp] - EvHeapAlloc // gcController.heapLive change [timestamp, heap alloc in bytes] - EvHeapGoal // gcController.heapGoal() change [timestamp, heap goal in bytes] - - // Annotations. - EvGoLabel // apply string label to current running goroutine [timestamp, label string ID] - EvUserTaskBegin // trace.NewTask [timestamp, internal task ID, internal parent task ID, name string ID, stack ID] - EvUserTaskEnd // end of a task [timestamp, internal task ID, stack ID] - EvUserRegionBegin // trace.{Start,With}Region [timestamp, internal task ID, name string ID, stack ID] - EvUserRegionEnd // trace.{End,With}Region [timestamp, internal task ID, name string ID, stack ID] - EvUserLog // trace.Log [timestamp, internal task ID, key string ID, value string ID, stack] - - // Coroutines. Added in Go 1.23. - EvGoSwitch // goroutine switch (coroswitch) [timestamp, goroutine ID, goroutine seq] - EvGoSwitchDestroy // goroutine switch and destroy [timestamp, goroutine ID, goroutine seq] - EvGoCreateBlocked // goroutine creation (starts blocked) [timestamp, new goroutine ID, new stack ID, stack ID] - - // GoStatus with stack. Added in Go 1.23. - EvGoStatusStack // goroutine status at the start of a generation, with a stack [timestamp, goroutine ID, M ID, status, stack ID] - - // Batch event for an experimental batch with a custom format. Added in Go 1.23. - EvExperimentalBatch // start of extra data [experiment ID, generation, M ID, timestamp, batch length, batch data...] -) - -// Experiments. -const ( - // AllocFree is the alloc-free events experiment. - AllocFree event.Experiment = 1 + iota -) - -// Experimental events. -const ( - _ event.Type = 127 + iota - - // Experimental events for AllocFree. - - // Experimental heap span events. Added in Go 1.23. - EvSpan // heap span exists [timestamp, id, npages, type/class] - EvSpanAlloc // heap span alloc [timestamp, id, npages, type/class] - EvSpanFree // heap span free [timestamp, id] - - // Experimental heap object events. Added in Go 1.23. - EvHeapObject // heap object exists [timestamp, id, type] - EvHeapObjectAlloc // heap object alloc [timestamp, id, type] - EvHeapObjectFree // heap object free [timestamp, id] - - // Experimental goroutine stack events. Added in Go 1.23. - EvGoroutineStack // stack exists [timestamp, id, order] - EvGoroutineStackAlloc // stack alloc [timestamp, id, order] - EvGoroutineStackFree // stack free [timestamp, id] -) - -// EventString returns the name of a Go 1.22 event. -func EventString(typ event.Type) string { - if int(typ) < len(specs) { - return specs[typ].Name - } - return fmt.Sprintf("Invalid(%d)", typ) -} - -func Specs() []event.Spec { - return specs[:] -} - -var specs = [...]event.Spec{ - // "Structural" Events. - EvEventBatch: event.Spec{ - Name: "EventBatch", - Args: []string{"gen", "m", "time", "size"}, - }, - EvStacks: event.Spec{ - Name: "Stacks", - }, - EvStack: event.Spec{ - Name: "Stack", - Args: []string{"id", "nframes"}, - IsStack: true, - }, - EvStrings: event.Spec{ - Name: "Strings", - }, - EvString: event.Spec{ - Name: "String", - Args: []string{"id"}, - HasData: true, - }, - EvCPUSamples: event.Spec{ - Name: "CPUSamples", - }, - EvCPUSample: event.Spec{ - Name: "CPUSample", - Args: []string{"time", "m", "p", "g", "stack"}, - // N.B. There's clearly a timestamp here, but these Events - // are special in that they don't appear in the regular - // M streams. - }, - EvFrequency: event.Spec{ - Name: "Frequency", - Args: []string{"freq"}, - }, - EvExperimentalBatch: event.Spec{ - Name: "ExperimentalBatch", - Args: []string{"exp", "gen", "m", "time"}, - HasData: true, // Easier to represent for raw readers. - }, - - // "Timed" Events. - EvProcsChange: event.Spec{ - Name: "ProcsChange", - Args: []string{"dt", "procs_value", "stack"}, - IsTimedEvent: true, - StackIDs: []int{2}, - }, - EvProcStart: event.Spec{ - Name: "ProcStart", - Args: []string{"dt", "p", "p_seq"}, - IsTimedEvent: true, - }, - EvProcStop: event.Spec{ - Name: "ProcStop", - Args: []string{"dt"}, - IsTimedEvent: true, - }, - EvProcSteal: event.Spec{ - Name: "ProcSteal", - Args: []string{"dt", "p", "p_seq", "m"}, - IsTimedEvent: true, - }, - EvProcStatus: event.Spec{ - Name: "ProcStatus", - Args: []string{"dt", "p", "pstatus"}, - IsTimedEvent: true, - }, - EvGoCreate: event.Spec{ - Name: "GoCreate", - Args: []string{"dt", "new_g", "new_stack", "stack"}, - IsTimedEvent: true, - StackIDs: []int{3, 2}, - }, - EvGoCreateSyscall: event.Spec{ - Name: "GoCreateSyscall", - Args: []string{"dt", "new_g"}, - IsTimedEvent: true, - }, - EvGoStart: event.Spec{ - Name: "GoStart", - Args: []string{"dt", "g", "g_seq"}, - IsTimedEvent: true, - }, - EvGoDestroy: event.Spec{ - Name: "GoDestroy", - Args: []string{"dt"}, - IsTimedEvent: true, - }, - EvGoDestroySyscall: event.Spec{ - Name: "GoDestroySyscall", - Args: []string{"dt"}, - IsTimedEvent: true, - }, - EvGoStop: event.Spec{ - Name: "GoStop", - Args: []string{"dt", "reason_string", "stack"}, - IsTimedEvent: true, - StackIDs: []int{2}, - StringIDs: []int{1}, - }, - EvGoBlock: event.Spec{ - Name: "GoBlock", - Args: []string{"dt", "reason_string", "stack"}, - IsTimedEvent: true, - StackIDs: []int{2}, - StringIDs: []int{1}, - }, - EvGoUnblock: event.Spec{ - Name: "GoUnblock", - Args: []string{"dt", "g", "g_seq", "stack"}, - IsTimedEvent: true, - StackIDs: []int{3}, - }, - EvGoSyscallBegin: event.Spec{ - Name: "GoSyscallBegin", - Args: []string{"dt", "p_seq", "stack"}, - IsTimedEvent: true, - StackIDs: []int{2}, - }, - EvGoSyscallEnd: event.Spec{ - Name: "GoSyscallEnd", - Args: []string{"dt"}, - StartEv: EvGoSyscallBegin, - IsTimedEvent: true, - }, - EvGoSyscallEndBlocked: event.Spec{ - Name: "GoSyscallEndBlocked", - Args: []string{"dt"}, - StartEv: EvGoSyscallBegin, - IsTimedEvent: true, - }, - EvGoStatus: event.Spec{ - Name: "GoStatus", - Args: []string{"dt", "g", "m", "gstatus"}, - IsTimedEvent: true, - }, - EvSTWBegin: event.Spec{ - Name: "STWBegin", - Args: []string{"dt", "kind_string", "stack"}, - IsTimedEvent: true, - StackIDs: []int{2}, - StringIDs: []int{1}, - }, - EvSTWEnd: event.Spec{ - Name: "STWEnd", - Args: []string{"dt"}, - StartEv: EvSTWBegin, - IsTimedEvent: true, - }, - EvGCActive: event.Spec{ - Name: "GCActive", - Args: []string{"dt", "gc_seq"}, - IsTimedEvent: true, - StartEv: EvGCBegin, - }, - EvGCBegin: event.Spec{ - Name: "GCBegin", - Args: []string{"dt", "gc_seq", "stack"}, - IsTimedEvent: true, - StackIDs: []int{2}, - }, - EvGCEnd: event.Spec{ - Name: "GCEnd", - Args: []string{"dt", "gc_seq"}, - StartEv: EvGCBegin, - IsTimedEvent: true, - }, - EvGCSweepActive: event.Spec{ - Name: "GCSweepActive", - Args: []string{"dt", "p"}, - StartEv: EvGCSweepBegin, - IsTimedEvent: true, - }, - EvGCSweepBegin: event.Spec{ - Name: "GCSweepBegin", - Args: []string{"dt", "stack"}, - IsTimedEvent: true, - StackIDs: []int{1}, - }, - EvGCSweepEnd: event.Spec{ - Name: "GCSweepEnd", - Args: []string{"dt", "swept_value", "reclaimed_value"}, - StartEv: EvGCSweepBegin, - IsTimedEvent: true, - }, - EvGCMarkAssistActive: event.Spec{ - Name: "GCMarkAssistActive", - Args: []string{"dt", "g"}, - StartEv: EvGCMarkAssistBegin, - IsTimedEvent: true, - }, - EvGCMarkAssistBegin: event.Spec{ - Name: "GCMarkAssistBegin", - Args: []string{"dt", "stack"}, - IsTimedEvent: true, - StackIDs: []int{1}, - }, - EvGCMarkAssistEnd: event.Spec{ - Name: "GCMarkAssistEnd", - Args: []string{"dt"}, - StartEv: EvGCMarkAssistBegin, - IsTimedEvent: true, - }, - EvHeapAlloc: event.Spec{ - Name: "HeapAlloc", - Args: []string{"dt", "heapalloc_value"}, - IsTimedEvent: true, - }, - EvHeapGoal: event.Spec{ - Name: "HeapGoal", - Args: []string{"dt", "heapgoal_value"}, - IsTimedEvent: true, - }, - EvGoLabel: event.Spec{ - Name: "GoLabel", - Args: []string{"dt", "label_string"}, - IsTimedEvent: true, - StringIDs: []int{1}, - }, - EvUserTaskBegin: event.Spec{ - Name: "UserTaskBegin", - Args: []string{"dt", "task", "parent_task", "name_string", "stack"}, - IsTimedEvent: true, - StackIDs: []int{4}, - StringIDs: []int{3}, - }, - EvUserTaskEnd: event.Spec{ - Name: "UserTaskEnd", - Args: []string{"dt", "task", "stack"}, - IsTimedEvent: true, - StackIDs: []int{2}, - }, - EvUserRegionBegin: event.Spec{ - Name: "UserRegionBegin", - Args: []string{"dt", "task", "name_string", "stack"}, - IsTimedEvent: true, - StackIDs: []int{3}, - StringIDs: []int{2}, - }, - EvUserRegionEnd: event.Spec{ - Name: "UserRegionEnd", - Args: []string{"dt", "task", "name_string", "stack"}, - StartEv: EvUserRegionBegin, - IsTimedEvent: true, - StackIDs: []int{3}, - StringIDs: []int{2}, - }, - EvUserLog: event.Spec{ - Name: "UserLog", - Args: []string{"dt", "task", "key_string", "value_string", "stack"}, - IsTimedEvent: true, - StackIDs: []int{4}, - StringIDs: []int{2, 3}, - }, - EvGoSwitch: event.Spec{ - Name: "GoSwitch", - Args: []string{"dt", "g", "g_seq"}, - IsTimedEvent: true, - }, - EvGoSwitchDestroy: event.Spec{ - Name: "GoSwitchDestroy", - Args: []string{"dt", "g", "g_seq"}, - IsTimedEvent: true, - }, - EvGoCreateBlocked: event.Spec{ - Name: "GoCreateBlocked", - Args: []string{"dt", "new_g", "new_stack", "stack"}, - IsTimedEvent: true, - StackIDs: []int{3, 2}, - }, - EvGoStatusStack: event.Spec{ - Name: "GoStatusStack", - Args: []string{"dt", "g", "m", "gstatus", "stack"}, - IsTimedEvent: true, - StackIDs: []int{4}, - }, - - // Experimental events. - - EvSpan: event.Spec{ - Name: "Span", - Args: []string{"dt", "id", "npages_value", "kindclass"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvSpanAlloc: event.Spec{ - Name: "SpanAlloc", - Args: []string{"dt", "id", "npages_value", "kindclass"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvSpanFree: event.Spec{ - Name: "SpanFree", - Args: []string{"dt", "id"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvHeapObject: event.Spec{ - Name: "HeapObject", - Args: []string{"dt", "id", "type"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvHeapObjectAlloc: event.Spec{ - Name: "HeapObjectAlloc", - Args: []string{"dt", "id", "type"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvHeapObjectFree: event.Spec{ - Name: "HeapObjectFree", - Args: []string{"dt", "id"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvGoroutineStack: event.Spec{ - Name: "GoroutineStack", - Args: []string{"dt", "id", "order"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvGoroutineStackAlloc: event.Spec{ - Name: "GoroutineStackAlloc", - Args: []string{"dt", "id", "order"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, - EvGoroutineStackFree: event.Spec{ - Name: "GoroutineStackFree", - Args: []string{"dt", "id"}, - IsTimedEvent: true, - Experiment: AllocFree, - }, -} - -type GoStatus uint8 - -const ( - GoBad GoStatus = iota - GoRunnable - GoRunning - GoSyscall - GoWaiting -) - -func (s GoStatus) String() string { - switch s { - case GoRunnable: - return "Runnable" - case GoRunning: - return "Running" - case GoSyscall: - return "Syscall" - case GoWaiting: - return "Waiting" - } - return "Bad" -} - -type ProcStatus uint8 - -const ( - ProcBad ProcStatus = iota - ProcRunning - ProcIdle - ProcSyscall - ProcSyscallAbandoned -) - -func (s ProcStatus) String() string { - switch s { - case ProcRunning: - return "Running" - case ProcIdle: - return "Idle" - case ProcSyscall: - return "Syscall" - } - return "Bad" -} - -const ( - // Various format-specific constants. - MaxBatchSize = 64 << 10 - MaxFramesPerStack = 128 - MaxStringSize = 1 << 10 -) diff --git a/src/internal/trace/v2/event/requirements.go b/src/internal/trace/v2/event/requirements.go deleted file mode 100644 index c5adf2e0c2..0000000000 --- a/src/internal/trace/v2/event/requirements.go +++ /dev/null @@ -1,26 +0,0 @@ -// 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. - -package event - -// SchedReqs is a set of constraints on what the scheduling -// context must look like. -type SchedReqs struct { - Thread Constraint - Proc Constraint - Goroutine Constraint -} - -// Constraint represents a various presence requirements. -type Constraint uint8 - -const ( - MustNotHave Constraint = iota - MayHave - MustHave -) - -// UserGoReqs is a common requirement among events that are running -// or are close to running user code. -var UserGoReqs = SchedReqs{Thread: MustHave, Proc: MustHave, Goroutine: MustHave} diff --git a/src/internal/trace/v2/event_test.go b/src/internal/trace/v2/event_test.go deleted file mode 100644 index c81a45185d..0000000000 --- a/src/internal/trace/v2/event_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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. - -package trace - -import "testing" - -func TestPanicEvent(t *testing.T) { - // Use a sync event for this because it doesn't have any extra metadata. - ev := syncEvent(nil, 0) - - mustPanic(t, func() { - _ = ev.Range() - }) - mustPanic(t, func() { - _ = ev.Metric() - }) - mustPanic(t, func() { - _ = ev.Log() - }) - mustPanic(t, func() { - _ = ev.Task() - }) - mustPanic(t, func() { - _ = ev.Region() - }) - mustPanic(t, func() { - _ = ev.Label() - }) - mustPanic(t, func() { - _ = ev.RangeAttributes() - }) -} - -func mustPanic(t *testing.T, f func()) { - defer func() { - if r := recover(); r == nil { - t.Fatal("failed to panic") - } - }() - f() -} diff --git a/src/internal/trace/v2/generation.go b/src/internal/trace/v2/generation.go deleted file mode 100644 index 0fdbfc3b20..0000000000 --- a/src/internal/trace/v2/generation.go +++ /dev/null @@ -1,446 +0,0 @@ -// 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. - -package trace - -import ( - "bufio" - "bytes" - "cmp" - "encoding/binary" - "fmt" - "io" - "slices" - "strings" - - "internal/trace/v2/event" - "internal/trace/v2/event/go122" -) - -// generation contains all the trace data for a single -// trace generation. It is purely data: it does not -// track any parse state nor does it contain a cursor -// into the generation. -type generation struct { - gen uint64 - batches map[ThreadID][]batch - cpuSamples []cpuSample - *evTable -} - -// spilledBatch represents a batch that was read out for the next generation, -// while reading the previous one. It's passed on when parsing the next -// generation. -type spilledBatch struct { - gen uint64 - *batch -} - -// readGeneration buffers and decodes the structural elements of a trace generation -// out of r. spill is the first batch of the new generation (already buffered and -// parsed from reading the last generation). Returns the generation and the first -// batch read of the next generation, if any. -// -// If gen is non-nil, it is valid and must be processed before handling the returned -// error. -func readGeneration(r *bufio.Reader, spill *spilledBatch) (*generation, *spilledBatch, error) { - g := &generation{ - evTable: &evTable{ - pcs: make(map[uint64]frame), - }, - batches: make(map[ThreadID][]batch), - } - // Process the spilled batch. - if spill != nil { - g.gen = spill.gen - if err := processBatch(g, *spill.batch); err != nil { - return nil, nil, err - } - spill = nil - } - // Read batches one at a time until we either hit EOF or - // the next generation. - var spillErr error - for { - b, gen, err := readBatch(r) - if err == io.EOF { - break - } - if err != nil { - if g.gen != 0 { - // This is an error reading the first batch of the next generation. - // This is fine. Let's forge ahead assuming that what we've got so - // far is fine. - spillErr = err - break - } - return nil, nil, err - } - if gen == 0 { - // 0 is a sentinel used by the runtime, so we'll never see it. - return nil, nil, fmt.Errorf("invalid generation number %d", gen) - } - if g.gen == 0 { - // Initialize gen. - g.gen = gen - } - if gen == g.gen+1 { // TODO: advance this the same way the runtime does. - spill = &spilledBatch{gen: gen, batch: &b} - break - } - if gen != g.gen { - // N.B. Fail as fast as possible if we see this. At first it - // may seem prudent to be fault-tolerant and assume we have a - // complete generation, parsing and returning that first. However, - // if the batches are mixed across generations then it's likely - // we won't be able to parse this generation correctly at all. - // Rather than return a cryptic error in that case, indicate the - // problem as soon as we see it. - return nil, nil, fmt.Errorf("generations out of order") - } - if err := processBatch(g, b); err != nil { - return nil, nil, err - } - } - - // Check some invariants. - if g.freq == 0 { - return nil, nil, fmt.Errorf("no frequency event found") - } - // N.B. Trust that the batch order is correct. We can't validate the batch order - // by timestamp because the timestamps could just be plain wrong. The source of - // truth is the order things appear in the trace and the partial order sequence - // numbers on certain events. If it turns out the batch order is actually incorrect - // we'll very likely fail to advance a partial order from the frontier. - - // Compactify stacks and strings for better lookup performance later. - g.stacks.compactify() - g.strings.compactify() - - // Validate stacks. - if err := validateStackStrings(&g.stacks, &g.strings, g.pcs); err != nil { - return nil, nil, err - } - - // Fix up the CPU sample timestamps, now that we have freq. - for i := range g.cpuSamples { - s := &g.cpuSamples[i] - s.time = g.freq.mul(timestamp(s.time)) - } - // Sort the CPU samples. - slices.SortFunc(g.cpuSamples, func(a, b cpuSample) int { - return cmp.Compare(a.time, b.time) - }) - return g, spill, spillErr -} - -// processBatch adds the batch to the generation. -func processBatch(g *generation, b batch) error { - switch { - case b.isStringsBatch(): - if err := addStrings(&g.strings, b); err != nil { - return err - } - case b.isStacksBatch(): - if err := addStacks(&g.stacks, g.pcs, b); err != nil { - return err - } - case b.isCPUSamplesBatch(): - samples, err := addCPUSamples(g.cpuSamples, b) - if err != nil { - return err - } - g.cpuSamples = samples - case b.isFreqBatch(): - freq, err := parseFreq(b) - if err != nil { - return err - } - if g.freq != 0 { - return fmt.Errorf("found multiple frequency events") - } - g.freq = freq - case b.exp != event.NoExperiment: - if err := addExperimentalData(g.expData, b); err != nil { - return err - } - default: - g.batches[b.m] = append(g.batches[b.m], b) - } - return nil -} - -// validateStackStrings makes sure all the string references in -// the stack table are present in the string table. -func validateStackStrings( - stacks *dataTable[stackID, stack], - strings *dataTable[stringID, string], - frames map[uint64]frame, -) error { - var err error - stacks.forEach(func(id stackID, stk stack) bool { - for _, pc := range stk.pcs { - frame, ok := frames[pc] - if !ok { - err = fmt.Errorf("found unknown pc %x for stack %d", pc, id) - return false - } - _, ok = strings.get(frame.funcID) - if !ok { - err = fmt.Errorf("found invalid func string ID %d for stack %d", frame.funcID, id) - return false - } - _, ok = strings.get(frame.fileID) - if !ok { - err = fmt.Errorf("found invalid file string ID %d for stack %d", frame.fileID, id) - return false - } - } - return true - }) - return err -} - -// addStrings takes a batch whose first byte is an EvStrings event -// (indicating that the batch contains only strings) and adds each -// string contained therein to the provided strings map. -func addStrings(stringTable *dataTable[stringID, string], b batch) error { - if !b.isStringsBatch() { - return fmt.Errorf("internal error: addStrings called on non-string batch") - } - r := bytes.NewReader(b.data) - hdr, err := r.ReadByte() // Consume the EvStrings byte. - if err != nil || event.Type(hdr) != go122.EvStrings { - return fmt.Errorf("missing strings batch header") - } - - var sb strings.Builder - for r.Len() != 0 { - // Read the header. - ev, err := r.ReadByte() - if err != nil { - return err - } - if event.Type(ev) != go122.EvString { - return fmt.Errorf("expected string event, got %d", ev) - } - - // Read the string's ID. - id, err := binary.ReadUvarint(r) - if err != nil { - return err - } - - // Read the string's length. - len, err := binary.ReadUvarint(r) - if err != nil { - return err - } - if len > go122.MaxStringSize { - return fmt.Errorf("invalid string size %d, maximum is %d", len, go122.MaxStringSize) - } - - // Copy out the string. - n, err := io.CopyN(&sb, r, int64(len)) - if n != int64(len) { - return fmt.Errorf("failed to read full string: read %d but wanted %d", n, len) - } - if err != nil { - return fmt.Errorf("copying string data: %w", err) - } - - // Add the string to the map. - s := sb.String() - sb.Reset() - if err := stringTable.insert(stringID(id), s); err != nil { - return err - } - } - return nil -} - -// addStacks takes a batch whose first byte is an EvStacks event -// (indicating that the batch contains only stacks) and adds each -// string contained therein to the provided stacks map. -func addStacks(stackTable *dataTable[stackID, stack], pcs map[uint64]frame, b batch) error { - if !b.isStacksBatch() { - return fmt.Errorf("internal error: addStacks called on non-stacks batch") - } - r := bytes.NewReader(b.data) - hdr, err := r.ReadByte() // Consume the EvStacks byte. - if err != nil || event.Type(hdr) != go122.EvStacks { - return fmt.Errorf("missing stacks batch header") - } - - for r.Len() != 0 { - // Read the header. - ev, err := r.ReadByte() - if err != nil { - return err - } - if event.Type(ev) != go122.EvStack { - return fmt.Errorf("expected stack event, got %d", ev) - } - - // Read the stack's ID. - id, err := binary.ReadUvarint(r) - if err != nil { - return err - } - - // Read how many frames are in each stack. - nFrames, err := binary.ReadUvarint(r) - if err != nil { - return err - } - if nFrames > go122.MaxFramesPerStack { - return fmt.Errorf("invalid stack size %d, maximum is %d", nFrames, go122.MaxFramesPerStack) - } - - // Each frame consists of 4 fields: pc, funcID (string), fileID (string), line. - frames := make([]uint64, 0, nFrames) - for i := uint64(0); i < nFrames; i++ { - // Read the frame data. - pc, err := binary.ReadUvarint(r) - if err != nil { - return fmt.Errorf("reading frame %d's PC for stack %d: %w", i+1, id, err) - } - funcID, err := binary.ReadUvarint(r) - if err != nil { - return fmt.Errorf("reading frame %d's funcID for stack %d: %w", i+1, id, err) - } - fileID, err := binary.ReadUvarint(r) - if err != nil { - return fmt.Errorf("reading frame %d's fileID for stack %d: %w", i+1, id, err) - } - line, err := binary.ReadUvarint(r) - if err != nil { - return fmt.Errorf("reading frame %d's line for stack %d: %w", i+1, id, err) - } - frames = append(frames, pc) - - if _, ok := pcs[pc]; !ok { - pcs[pc] = frame{ - pc: pc, - funcID: stringID(funcID), - fileID: stringID(fileID), - line: line, - } - } - } - - // Add the stack to the map. - if err := stackTable.insert(stackID(id), stack{pcs: frames}); err != nil { - return err - } - } - return nil -} - -// addCPUSamples takes a batch whose first byte is an EvCPUSamples event -// (indicating that the batch contains only CPU samples) and adds each -// sample contained therein to the provided samples list. -func addCPUSamples(samples []cpuSample, b batch) ([]cpuSample, error) { - if !b.isCPUSamplesBatch() { - return nil, fmt.Errorf("internal error: addCPUSamples called on non-CPU-sample batch") - } - r := bytes.NewReader(b.data) - hdr, err := r.ReadByte() // Consume the EvCPUSamples byte. - if err != nil || event.Type(hdr) != go122.EvCPUSamples { - return nil, fmt.Errorf("missing CPU samples batch header") - } - - for r.Len() != 0 { - // Read the header. - ev, err := r.ReadByte() - if err != nil { - return nil, err - } - if event.Type(ev) != go122.EvCPUSample { - return nil, fmt.Errorf("expected CPU sample event, got %d", ev) - } - - // Read the sample's timestamp. - ts, err := binary.ReadUvarint(r) - if err != nil { - return nil, err - } - - // Read the sample's M. - m, err := binary.ReadUvarint(r) - if err != nil { - return nil, err - } - mid := ThreadID(m) - - // Read the sample's P. - p, err := binary.ReadUvarint(r) - if err != nil { - return nil, err - } - pid := ProcID(p) - - // Read the sample's G. - g, err := binary.ReadUvarint(r) - if err != nil { - return nil, err - } - goid := GoID(g) - if g == 0 { - goid = NoGoroutine - } - - // Read the sample's stack. - s, err := binary.ReadUvarint(r) - if err != nil { - return nil, err - } - - // Add the sample to the slice. - samples = append(samples, cpuSample{ - schedCtx: schedCtx{ - M: mid, - P: pid, - G: goid, - }, - time: Time(ts), // N.B. this is really a "timestamp," not a Time. - stack: stackID(s), - }) - } - return samples, nil -} - -// parseFreq parses out a lone EvFrequency from a batch. -func parseFreq(b batch) (frequency, error) { - if !b.isFreqBatch() { - return 0, fmt.Errorf("internal error: parseFreq called on non-frequency batch") - } - r := bytes.NewReader(b.data) - r.ReadByte() // Consume the EvFrequency byte. - - // Read the frequency. It'll come out as timestamp units per second. - f, err := binary.ReadUvarint(r) - if err != nil { - return 0, err - } - // Convert to nanoseconds per timestamp unit. - return frequency(1.0 / (float64(f) / 1e9)), nil -} - -// addExperimentalData takes an experimental batch and adds it to the ExperimentalData -// for the experiment its a part of. -func addExperimentalData(expData map[event.Experiment]*ExperimentalData, b batch) error { - if b.exp == event.NoExperiment { - return fmt.Errorf("internal error: addExperimentalData called on non-experimental batch") - } - ed, ok := expData[b.exp] - if !ok { - ed = new(ExperimentalData) - } - ed.Batches = append(ed.Batches, ExperimentalBatch{ - Thread: b.m, - Data: b.data, - }) - return nil -} diff --git a/src/internal/trace/v2/internal/oldtrace/order.go b/src/internal/trace/v2/internal/oldtrace/order.go deleted file mode 100644 index 4cd3def211..0000000000 --- a/src/internal/trace/v2/internal/oldtrace/order.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2024 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 oldtrace - -import "errors" - -type orderEvent struct { - ev Event - proc *proc -} - -type gStatus int - -type gState struct { - seq uint64 - status gStatus -} - -const ( - gDead gStatus = iota - gRunnable - gRunning - gWaiting - - unordered = ^uint64(0) - garbage = ^uint64(0) - 1 - noseq = ^uint64(0) - seqinc = ^uint64(0) - 1 -) - -// stateTransition returns goroutine state (sequence and status) when the event -// becomes ready for merging (init) and the goroutine state after the event (next). -func stateTransition(ev *Event) (g uint64, init, next gState) { - // Note that we have an explicit return in each case, as that produces slightly better code (tested on Go 1.19). - - switch ev.Type { - case EvGoCreate: - g = ev.Args[0] - init = gState{0, gDead} - next = gState{1, gRunnable} - return - case EvGoWaiting, EvGoInSyscall: - g = ev.G - init = gState{1, gRunnable} - next = gState{2, gWaiting} - return - case EvGoStart, EvGoStartLabel: - g = ev.G - init = gState{ev.Args[1], gRunnable} - next = gState{ev.Args[1] + 1, gRunning} - return - case EvGoStartLocal: - // noseq means that this event is ready for merging as soon as - // frontier reaches it (EvGoStartLocal is emitted on the same P - // as the corresponding EvGoCreate/EvGoUnblock, and thus the latter - // is already merged). - // seqinc is a stub for cases when event increments g sequence, - // but since we don't know current seq we also don't know next seq. - g = ev.G - init = gState{noseq, gRunnable} - next = gState{seqinc, gRunning} - return - case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, - EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep, - EvGoSysBlock, EvGoBlockGC: - g = ev.G - init = gState{noseq, gRunning} - next = gState{noseq, gWaiting} - return - case EvGoSched, EvGoPreempt: - g = ev.G - init = gState{noseq, gRunning} - next = gState{noseq, gRunnable} - return - case EvGoUnblock, EvGoSysExit: - g = ev.Args[0] - init = gState{ev.Args[1], gWaiting} - next = gState{ev.Args[1] + 1, gRunnable} - return - case EvGoUnblockLocal, EvGoSysExitLocal: - g = ev.Args[0] - init = gState{noseq, gWaiting} - next = gState{seqinc, gRunnable} - return - case EvGCStart: - g = garbage - init = gState{ev.Args[0], gDead} - next = gState{ev.Args[0] + 1, gDead} - return - default: - // no ordering requirements - g = unordered - return - } -} - -func transitionReady(g uint64, curr, init gState) bool { - return g == unordered || (init.seq == noseq || init.seq == curr.seq) && init.status == curr.status -} - -func transition(gs map[uint64]gState, g uint64, init, next gState) error { - if g == unordered { - return nil - } - curr := gs[g] - if !transitionReady(g, curr, init) { - // See comment near the call to transition, where we're building the frontier, for details on how this could - // possibly happen. - return errors.New("encountered impossible goroutine state transition") - } - switch next.seq { - case noseq: - next.seq = curr.seq - case seqinc: - next.seq = curr.seq + 1 - } - gs[g] = next - return nil -} - -type orderEventList []orderEvent - -func (l *orderEventList) Less(i, j int) bool { - return (*l)[i].ev.Ts < (*l)[j].ev.Ts -} - -type eventList []Event - -func (l *eventList) Len() int { - return len(*l) -} - -func (l *eventList) Less(i, j int) bool { - return (*l)[i].Ts < (*l)[j].Ts -} - -func (l *eventList) Swap(i, j int) { - (*l)[i], (*l)[j] = (*l)[j], (*l)[i] -} - -func (h *orderEventList) Push(x orderEvent) { - *h = append(*h, x) - heapUp(h, len(*h)-1) -} - -func (h *orderEventList) Pop() orderEvent { - n := len(*h) - 1 - (*h)[0], (*h)[n] = (*h)[n], (*h)[0] - heapDown(h, 0, n) - x := (*h)[len(*h)-1] - *h = (*h)[:len(*h)-1] - return x -} - -func heapUp(h *orderEventList, j int) { - for { - i := (j - 1) / 2 // parent - if i == j || !h.Less(j, i) { - break - } - (*h)[i], (*h)[j] = (*h)[j], (*h)[i] - j = i - } -} - -func heapDown(h *orderEventList, i0, n int) bool { - i := i0 - for { - j1 := 2*i + 1 - if j1 >= n || j1 < 0 { // j1 < 0 after int overflow - break - } - j := j1 // left child - if j2 := j1 + 1; j2 < n && h.Less(j2, j1) { - j = j2 // = 2*i + 2 // right child - } - if !h.Less(j, i) { - break - } - (*h)[i], (*h)[j] = (*h)[j], (*h)[i] - i = j - } - return i > i0 -} diff --git a/src/internal/trace/v2/internal/oldtrace/parser.go b/src/internal/trace/v2/internal/oldtrace/parser.go deleted file mode 100644 index da1079b34b..0000000000 --- a/src/internal/trace/v2/internal/oldtrace/parser.go +++ /dev/null @@ -1,1540 +0,0 @@ -// Copyright 2024 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 oldtrace implements a parser for Go execution traces from versions -// 1.11–1.21. -// -// The package started as a copy of Go 1.19's internal/trace, but has been -// optimized to be faster while using less memory and fewer allocations. It has -// been further modified for the specific purpose of converting traces to the -// new 1.22+ format. -package oldtrace - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "internal/trace/v2/event" - "internal/trace/v2/version" - "io" - "math" - "sort" -) - -// Timestamp represents a count of nanoseconds since the beginning of the trace. -// They can only be meaningfully compared with other timestamps from the same -// trace. -type Timestamp int64 - -// Event describes one event in the trace. -type Event struct { - // The Event type is carefully laid out to optimize its size and to avoid - // pointers, the latter so that the garbage collector won't have to scan any - // memory of our millions of events. - - Ts Timestamp // timestamp in nanoseconds - G uint64 // G on which the event happened - Args [4]uint64 // event-type-specific arguments - StkID uint32 // unique stack ID - P int32 // P on which the event happened (can be a real P or one of TimerP, NetpollP, SyscallP) - Type event.Type // one of Ev* -} - -// Frame is a frame in stack traces. -type Frame struct { - PC uint64 - // string ID of the function name - Fn uint64 - // string ID of the file name - File uint64 - Line int -} - -const ( - // Special P identifiers: - FakeP = 1000000 + iota - TimerP // contains timer unblocks - NetpollP // contains network unblocks - SyscallP // contains returns from syscalls - GCP // contains GC state - ProfileP // contains recording of CPU profile samples -) - -// Trace is the result of Parse. -type Trace struct { - Version version.Version - - // Events is the sorted list of Events in the trace. - Events Events - // Stacks is the stack traces (stored as slices of PCs), keyed by stack IDs - // from the trace. - Stacks map[uint32][]uint64 - PCs map[uint64]Frame - Strings map[uint64]string - InlineStrings []string -} - -// batchOffset records the byte offset of, and number of events in, a batch. A -// batch is a sequence of events emitted by a P. Events within a single batch -// are sorted by time. -type batchOffset struct { - offset int - numEvents int -} - -type parser struct { - ver version.Version - data []byte - off int - - strings map[uint64]string - inlineStrings []string - inlineStringsMapping map[string]int - // map from Ps to their batch offsets - batchOffsets map[int32][]batchOffset - stacks map[uint32][]uint64 - stacksData []uint64 - ticksPerSec int64 - pcs map[uint64]Frame - cpuSamples []Event - timerGoids map[uint64]bool - - // state for readRawEvent - args []uint64 - - // state for parseEvent - lastTs Timestamp - lastG uint64 - // map from Ps to the last Gs that ran on them - lastGs map[int32]uint64 - lastP int32 -} - -func (p *parser) discard(n uint64) bool { - if n > math.MaxInt { - return false - } - if noff := p.off + int(n); noff < p.off || noff > len(p.data) { - return false - } else { - p.off = noff - } - return true -} - -func newParser(r io.Reader, ver version.Version) (*parser, error) { - var buf []byte - if seeker, ok := r.(io.Seeker); ok { - // Determine the size of the reader so that we can allocate a buffer - // without having to grow it later. - cur, err := seeker.Seek(0, io.SeekCurrent) - if err != nil { - return nil, err - } - end, err := seeker.Seek(0, io.SeekEnd) - if err != nil { - return nil, err - } - _, err = seeker.Seek(cur, io.SeekStart) - if err != nil { - return nil, err - } - - buf = make([]byte, end-cur) - _, err = io.ReadFull(r, buf) - if err != nil { - return nil, err - } - } else { - var err error - buf, err = io.ReadAll(r) - if err != nil { - return nil, err - } - } - return &parser{data: buf, ver: ver, timerGoids: make(map[uint64]bool)}, nil -} - -// Parse parses Go execution traces from versions 1.11–1.21. The provided reader -// will be read to completion and the entire trace will be materialized in -// memory. That is, this function does not allow incremental parsing. -// -// The reader has to be positioned just after the trace header and vers needs to -// be the version of the trace. This can be achieved by using -// version.ReadHeader. -func Parse(r io.Reader, vers version.Version) (Trace, error) { - // We accept the version as an argument because internal/trace/v2 will have - // already read the version to determine which parser to use. - p, err := newParser(r, vers) - if err != nil { - return Trace{}, err - } - return p.parse() -} - -// parse parses, post-processes and verifies the trace. -func (p *parser) parse() (Trace, error) { - defer func() { - p.data = nil - }() - - // We parse a trace by running the following steps in order: - // - // 1. In the initial pass we collect information about batches (their - // locations and sizes.) We also parse CPU profiling samples in this - // step, simply to reduce the number of full passes that we need. - // - // 2. In the second pass we parse batches and merge them into a globally - // ordered event stream. This uses the batch information from the first - // pass to quickly find batches. - // - // 3. After all events have been parsed we convert their timestamps from CPU - // ticks to wall time. Furthermore we move timers and syscalls to - // dedicated, fake Ps. - // - // 4. Finally, we validate the trace. - - p.strings = make(map[uint64]string) - p.batchOffsets = make(map[int32][]batchOffset) - p.lastGs = make(map[int32]uint64) - p.stacks = make(map[uint32][]uint64) - p.pcs = make(map[uint64]Frame) - p.inlineStringsMapping = make(map[string]int) - - if err := p.collectBatchesAndCPUSamples(); err != nil { - return Trace{}, err - } - - events, err := p.parseEventBatches() - if err != nil { - return Trace{}, err - } - - if p.ticksPerSec == 0 { - return Trace{}, errors.New("no EvFrequency event") - } - - if events.Len() > 0 { - // Translate cpu ticks to real time. - minTs := events.Ptr(0).Ts - // Use floating point to avoid integer overflows. - freq := 1e9 / float64(p.ticksPerSec) - for i := 0; i < events.Len(); i++ { - ev := events.Ptr(i) - ev.Ts = Timestamp(float64(ev.Ts-minTs) * freq) - // Move timers and syscalls to separate fake Ps. - if p.timerGoids[ev.G] && ev.Type == EvGoUnblock { - ev.P = TimerP - } - if ev.Type == EvGoSysExit { - ev.P = SyscallP - } - } - } - - if err := p.postProcessTrace(events); err != nil { - return Trace{}, err - } - - res := Trace{ - Version: p.ver, - Events: events, - Stacks: p.stacks, - Strings: p.strings, - InlineStrings: p.inlineStrings, - PCs: p.pcs, - } - return res, nil -} - -// rawEvent is a helper type used during parsing. -type rawEvent struct { - typ event.Type - args []uint64 - sargs []string - - // if typ == EvBatch, these fields describe the batch. - batchPid int32 - batchOffset int -} - -type proc struct { - pid int32 - // the remaining events in the current batch - events []Event - // buffer for reading batches into, aliased by proc.events - buf []Event - - // there are no more batches left - done bool -} - -const eventsBucketSize = 524288 // 32 MiB of events - -type Events struct { - // Events is a slice of slices that grows one slice of size eventsBucketSize - // at a time. This avoids the O(n) cost of slice growth in append, and - // additionally allows consumers to drop references to parts of the data, - // freeing memory piecewise. - n int - buckets []*[eventsBucketSize]Event - off int -} - -// grow grows the slice by one and returns a pointer to the new element, without -// overwriting it. -func (l *Events) grow() *Event { - a, b := l.index(l.n) - if a >= len(l.buckets) { - l.buckets = append(l.buckets, new([eventsBucketSize]Event)) - } - ptr := &l.buckets[a][b] - l.n++ - return ptr -} - -// append appends v to the slice and returns a pointer to the new element. -func (l *Events) append(v Event) *Event { - ptr := l.grow() - *ptr = v - return ptr -} - -func (l *Events) Ptr(i int) *Event { - a, b := l.index(i + l.off) - return &l.buckets[a][b] -} - -func (l *Events) index(i int) (int, int) { - // Doing the division on uint instead of int compiles this function to a - // shift and an AND (for power of 2 bucket sizes), versus a whole bunch of - // instructions for int. - return int(uint(i) / eventsBucketSize), int(uint(i) % eventsBucketSize) -} - -func (l *Events) Len() int { - return l.n - l.off -} - -func (l *Events) Less(i, j int) bool { - return l.Ptr(i).Ts < l.Ptr(j).Ts -} - -func (l *Events) Swap(i, j int) { - *l.Ptr(i), *l.Ptr(j) = *l.Ptr(j), *l.Ptr(i) -} - -func (l *Events) Pop() (*Event, bool) { - if l.off == l.n { - return nil, false - } - a, b := l.index(l.off) - ptr := &l.buckets[a][b] - l.off++ - if b == eventsBucketSize-1 || l.off == l.n { - // We've consumed the last event from the bucket, so drop the bucket and - // allow GC to collect it. - l.buckets[a] = nil - } - return ptr, true -} - -func (l *Events) All() func(yield func(ev *Event) bool) { - return func(yield func(ev *Event) bool) { - for i := 0; i < l.Len(); i++ { - a, b := l.index(i + l.off) - ptr := &l.buckets[a][b] - if !yield(ptr) { - return - } - } - } -} - -// parseEventBatches reads per-P event batches and merges them into a single, consistent -// stream. The high level idea is as follows. Events within an individual batch -// are in correct order, because they are emitted by a single P. So we need to -// produce a correct interleaving of the batches. To do this we take first -// unmerged event from each batch (frontier). Then choose subset that is "ready" -// to be merged, that is, events for which all dependencies are already merged. -// Then we choose event with the lowest timestamp from the subset, merge it and -// repeat. This approach ensures that we form a consistent stream even if -// timestamps are incorrect (condition observed on some machines). -func (p *parser) parseEventBatches() (Events, error) { - // The ordering of CPU profile sample events in the data stream is based on - // when each run of the signal handler was able to acquire the spinlock, - // with original timestamps corresponding to when ReadTrace pulled the data - // off of the profBuf queue. Re-sort them by the timestamp we captured - // inside the signal handler. - sort.Sort((*eventList)(&p.cpuSamples)) - - allProcs := make([]proc, 0, len(p.batchOffsets)) - for pid := range p.batchOffsets { - allProcs = append(allProcs, proc{pid: pid}) - } - allProcs = append(allProcs, proc{pid: ProfileP, events: p.cpuSamples}) - - events := Events{} - - // Merge events as long as at least one P has more events - gs := make(map[uint64]gState) - // Note: technically we don't need a priority queue here. We're only ever - // interested in the earliest elligible event, which means we just have to - // track the smallest element. However, in practice, the priority queue - // performs better, because for each event we only have to compute its state - // transition once, not on each iteration. If it was elligible before, it'll - // already be in the queue. Furthermore, on average, we only have one P to - // look at in each iteration, because all other Ps are already in the queue. - var frontier orderEventList - - availableProcs := make([]*proc, len(allProcs)) - for i := range allProcs { - availableProcs[i] = &allProcs[i] - } - for { - pidLoop: - for i := 0; i < len(availableProcs); i++ { - proc := availableProcs[i] - - for len(proc.events) == 0 { - // Call loadBatch in a loop because sometimes batches are empty - evs, err := p.loadBatch(proc.pid, proc.buf[:0]) - proc.buf = evs[:0] - if err == io.EOF { - // This P has no more events - proc.done = true - availableProcs[i], availableProcs[len(availableProcs)-1] = availableProcs[len(availableProcs)-1], availableProcs[i] - availableProcs = availableProcs[:len(availableProcs)-1] - // We swapped the element at i with another proc, so look at - // the index again - i-- - continue pidLoop - } else if err != nil { - return Events{}, err - } else { - proc.events = evs - } - } - - ev := &proc.events[0] - g, init, _ := stateTransition(ev) - - // TODO(dh): This implementation matches the behavior of the - // upstream 'go tool trace', and works in practice, but has run into - // the following inconsistency during fuzzing: what happens if - // multiple Ps have events for the same G? While building the - // frontier we will check all of the events against the current - // state of the G. However, when we process the frontier, the state - // of the G changes, and a transition that was valid while building - // the frontier may no longer be valid when processing the frontier. - // Is this something that can happen for real, valid traces, or is - // this only possible with corrupt data? - if !transitionReady(g, gs[g], init) { - continue - } - proc.events = proc.events[1:] - availableProcs[i], availableProcs[len(availableProcs)-1] = availableProcs[len(availableProcs)-1], availableProcs[i] - availableProcs = availableProcs[:len(availableProcs)-1] - frontier.Push(orderEvent{*ev, proc}) - - // We swapped the element at i with another proc, so look at the - // index again - i-- - } - - if len(frontier) == 0 { - for i := range allProcs { - if !allProcs[i].done { - return Events{}, fmt.Errorf("no consistent ordering of events possible") - } - } - break - } - f := frontier.Pop() - - // We're computing the state transition twice, once when computing the - // frontier, and now to apply the transition. This is fine because - // stateTransition is a pure function. Computing it again is cheaper - // than storing large items in the frontier. - g, init, next := stateTransition(&f.ev) - - // Get rid of "Local" events, they are intended merely for ordering. - switch f.ev.Type { - case EvGoStartLocal: - f.ev.Type = EvGoStart - case EvGoUnblockLocal: - f.ev.Type = EvGoUnblock - case EvGoSysExitLocal: - f.ev.Type = EvGoSysExit - } - events.append(f.ev) - - if err := transition(gs, g, init, next); err != nil { - return Events{}, err - } - availableProcs = append(availableProcs, f.proc) - } - - // At this point we have a consistent stream of events. Make sure time - // stamps respect the ordering. The tests will skip (not fail) the test case - // if they see this error. - if !sort.IsSorted(&events) { - return Events{}, ErrTimeOrder - } - - // The last part is giving correct timestamps to EvGoSysExit events. The - // problem with EvGoSysExit is that actual syscall exit timestamp - // (ev.Args[2]) is potentially acquired long before event emission. So far - // we've used timestamp of event emission (ev.Ts). We could not set ev.Ts = - // ev.Args[2] earlier, because it would produce seemingly broken timestamps - // (misplaced event). We also can't simply update the timestamp and resort - // events, because if timestamps are broken we will misplace the event and - // later report logically broken trace (instead of reporting broken - // timestamps). - lastSysBlock := make(map[uint64]Timestamp) - for i := 0; i < events.Len(); i++ { - ev := events.Ptr(i) - switch ev.Type { - case EvGoSysBlock, EvGoInSyscall: - lastSysBlock[ev.G] = ev.Ts - case EvGoSysExit: - ts := Timestamp(ev.Args[2]) - if ts == 0 { - continue - } - block := lastSysBlock[ev.G] - if block == 0 { - return Events{}, fmt.Errorf("stray syscall exit") - } - if ts < block { - return Events{}, ErrTimeOrder - } - ev.Ts = ts - } - } - sort.Stable(&events) - - return events, nil -} - -// collectBatchesAndCPUSamples records the offsets of batches and parses CPU samples. -func (p *parser) collectBatchesAndCPUSamples() error { - // Read events. - var raw rawEvent - var curP int32 - for n := uint64(0); ; n++ { - err := p.readRawEvent(skipArgs|skipStrings, &raw) - if err == io.EOF { - break - } - if err != nil { - return err - } - if raw.typ == EvNone { - continue - } - - if raw.typ == EvBatch { - bo := batchOffset{offset: raw.batchOffset} - p.batchOffsets[raw.batchPid] = append(p.batchOffsets[raw.batchPid], bo) - curP = raw.batchPid - } - - batches := p.batchOffsets[curP] - if len(batches) == 0 { - return fmt.Errorf("read event %d with current P of %d, but P has no batches yet", - raw.typ, curP) - } - batches[len(batches)-1].numEvents++ - - if raw.typ == EvCPUSample { - e := Event{Type: raw.typ} - - argOffset := 1 - narg := raw.argNum() - if len(raw.args) != narg { - return fmt.Errorf("CPU sample has wrong number of arguments: want %d, got %d", narg, len(raw.args)) - } - for i := argOffset; i < narg; i++ { - if i == narg-1 { - e.StkID = uint32(raw.args[i]) - } else { - e.Args[i-argOffset] = raw.args[i] - } - } - - e.Ts = Timestamp(e.Args[0]) - e.P = int32(e.Args[1]) - e.G = e.Args[2] - e.Args[0] = 0 - - // Most events are written out by the active P at the exact moment - // they describe. CPU profile samples are different because they're - // written to the tracing log after some delay, by a separate worker - // goroutine, into a separate buffer. - // - // We keep these in their own batch until all of the batches are - // merged in timestamp order. We also (right before the merge) - // re-sort these events by the timestamp captured in the profiling - // signal handler. - // - // Note that we're not concerned about the memory usage of storing - // all CPU samples during the indexing phase. There are orders of - // magnitude fewer CPU samples than runtime events. - p.cpuSamples = append(p.cpuSamples, e) - } - } - - return nil -} - -const ( - skipArgs = 1 << iota - skipStrings -) - -func (p *parser) readByte() (byte, bool) { - if p.off < len(p.data) && p.off >= 0 { - b := p.data[p.off] - p.off++ - return b, true - } else { - return 0, false - } -} - -func (p *parser) readFull(n int) ([]byte, error) { - if p.off >= len(p.data) || p.off < 0 || p.off+n > len(p.data) { - // p.off < 0 is impossible but makes BCE happy. - // - // We do fail outright if there's not enough data, we don't care about - // partial results. - return nil, io.ErrUnexpectedEOF - } - buf := p.data[p.off : p.off+n] - p.off += n - return buf, nil -} - -// readRawEvent reads a raw event into ev. The slices in ev are only valid until -// the next call to readRawEvent, even when storing to a different location. -func (p *parser) readRawEvent(flags uint, ev *rawEvent) error { - // The number of arguments is encoded using two bits and can thus only - // represent the values 0–3. The value 3 (on the wire) indicates that - // arguments are prefixed by their byte length, to encode >=3 arguments. - const inlineArgs = 3 - - // Read event type and number of arguments (1 byte). - b, ok := p.readByte() - if !ok { - return io.EOF - } - typ := event.Type(b << 2 >> 2) - // Most events have a timestamp before the actual arguments, so we add 1 and - // parse it like it's the first argument. EvString has a special format and - // the number of arguments doesn't matter. EvBatch writes '1' as the number - // of arguments, but actually has two: a pid and a timestamp, but here the - // timestamp is the second argument, not the first; adding 1 happens to come - // up with the correct number, but it doesn't matter, because EvBatch has - // custom logic for parsing. - // - // Note that because we're adding 1, inlineArgs == 3 describes the largest - // number of logical arguments that isn't length-prefixed, even though the - // value 3 on the wire indicates length-prefixing. For us, that becomes narg - // == 4. - narg := b>>6 + 1 - if typ == EvNone || typ >= EvCount || EventDescriptions[typ].minVersion > p.ver { - return fmt.Errorf("unknown event type %d", typ) - } - - switch typ { - case EvString: - if flags&skipStrings != 0 { - // String dictionary entry [ID, length, string]. - if _, err := p.readVal(); err != nil { - return errMalformedVarint - } - ln, err := p.readVal() - if err != nil { - return err - } - if !p.discard(ln) { - return fmt.Errorf("failed to read trace: %w", io.EOF) - } - } else { - // String dictionary entry [ID, length, string]. - id, err := p.readVal() - if err != nil { - return err - } - if id == 0 { - return errors.New("string has invalid id 0") - } - if p.strings[id] != "" { - return fmt.Errorf("string has duplicate id %d", id) - } - var ln uint64 - ln, err = p.readVal() - if err != nil { - return err - } - if ln == 0 { - return errors.New("string has invalid length 0") - } - if ln > 1e6 { - return fmt.Errorf("string has too large length %d", ln) - } - buf, err := p.readFull(int(ln)) - if err != nil { - return fmt.Errorf("failed to read trace: %w", err) - } - p.strings[id] = string(buf) - } - - ev.typ = EvNone - return nil - case EvBatch: - if want := byte(2); narg != want { - return fmt.Errorf("EvBatch has wrong number of arguments: got %d, want %d", narg, want) - } - - // -1 because we've already read the first byte of the batch - off := p.off - 1 - - pid, err := p.readVal() - if err != nil { - return err - } - if pid != math.MaxUint64 && pid > math.MaxInt32 { - return fmt.Errorf("processor ID %d is larger than maximum of %d", pid, uint64(math.MaxUint)) - } - - var pid32 int32 - if pid == math.MaxUint64 { - pid32 = -1 - } else { - pid32 = int32(pid) - } - - v, err := p.readVal() - if err != nil { - return err - } - - *ev = rawEvent{ - typ: EvBatch, - args: p.args[:0], - batchPid: pid32, - batchOffset: off, - } - ev.args = append(ev.args, pid, v) - return nil - default: - *ev = rawEvent{typ: typ, args: p.args[:0]} - if narg <= inlineArgs { - if flags&skipArgs == 0 { - for i := 0; i < int(narg); i++ { - v, err := p.readVal() - if err != nil { - return fmt.Errorf("failed to read event %d argument: %w", typ, err) - } - ev.args = append(ev.args, v) - } - } else { - for i := 0; i < int(narg); i++ { - if _, err := p.readVal(); err != nil { - return fmt.Errorf("failed to read event %d argument: %w", typ, errMalformedVarint) - } - } - } - } else { - // More than inlineArgs args, the first value is length of the event - // in bytes. - v, err := p.readVal() - if err != nil { - return fmt.Errorf("failed to read event %d argument: %w", typ, err) - } - - if limit := uint64(2048); v > limit { - // At the time of Go 1.19, v seems to be at most 128. Set 2048 - // as a generous upper limit and guard against malformed traces. - return fmt.Errorf("failed to read event %d argument: length-prefixed argument too big: %d bytes, limit is %d", typ, v, limit) - } - - if flags&skipArgs == 0 || typ == EvCPUSample { - buf, err := p.readFull(int(v)) - if err != nil { - return fmt.Errorf("failed to read trace: %w", err) - } - for len(buf) > 0 { - var v uint64 - v, buf, err = readValFrom(buf) - if err != nil { - return err - } - ev.args = append(ev.args, v) - } - } else { - // Skip over arguments - if !p.discard(v) { - return fmt.Errorf("failed to read trace: %w", io.EOF) - } - } - if typ == EvUserLog { - // EvUserLog records are followed by a value string - if flags&skipArgs == 0 { - // Read string - s, err := p.readStr() - if err != nil { - return err - } - ev.sargs = append(ev.sargs, s) - } else { - // Skip string - v, err := p.readVal() - if err != nil { - return err - } - if !p.discard(v) { - return io.EOF - } - } - } - } - - p.args = ev.args[:0] - return nil - } -} - -// loadBatch loads the next batch for pid and appends its contents to to events. -func (p *parser) loadBatch(pid int32, events []Event) ([]Event, error) { - offsets := p.batchOffsets[pid] - if len(offsets) == 0 { - return nil, io.EOF - } - n := offsets[0].numEvents - offset := offsets[0].offset - offsets = offsets[1:] - p.batchOffsets[pid] = offsets - - p.off = offset - - if cap(events) < n { - events = make([]Event, 0, n) - } - - gotHeader := false - var raw rawEvent - var ev Event - for { - err := p.readRawEvent(0, &raw) - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - if raw.typ == EvNone || raw.typ == EvCPUSample { - continue - } - if raw.typ == EvBatch { - if gotHeader { - break - } else { - gotHeader = true - } - } - - err = p.parseEvent(&raw, &ev) - if err != nil { - return nil, err - } - if ev.Type != EvNone { - events = append(events, ev) - } - } - - return events, nil -} - -func (p *parser) readStr() (s string, err error) { - sz, err := p.readVal() - if err != nil { - return "", err - } - if sz == 0 { - return "", nil - } - if sz > 1e6 { - return "", fmt.Errorf("string is too large (len=%d)", sz) - } - buf, err := p.readFull(int(sz)) - if err != nil { - return "", fmt.Errorf("failed to read trace: %w", err) - } - return string(buf), nil -} - -// parseEvent transforms raw events into events. -// It does analyze and verify per-event-type arguments. -func (p *parser) parseEvent(raw *rawEvent, ev *Event) error { - desc := &EventDescriptions[raw.typ] - if desc.Name == "" { - return fmt.Errorf("missing description for event type %d", raw.typ) - } - narg := raw.argNum() - if len(raw.args) != narg { - return fmt.Errorf("%s has wrong number of arguments: want %d, got %d", desc.Name, narg, len(raw.args)) - } - switch raw.typ { - case EvBatch: - p.lastGs[p.lastP] = p.lastG - if raw.args[0] != math.MaxUint64 && raw.args[0] > math.MaxInt32 { - return fmt.Errorf("processor ID %d is larger than maximum of %d", raw.args[0], uint64(math.MaxInt32)) - } - if raw.args[0] == math.MaxUint64 { - p.lastP = -1 - } else { - p.lastP = int32(raw.args[0]) - } - p.lastG = p.lastGs[p.lastP] - p.lastTs = Timestamp(raw.args[1]) - case EvFrequency: - p.ticksPerSec = int64(raw.args[0]) - if p.ticksPerSec <= 0 { - // The most likely cause for this is tick skew on different CPUs. - // For example, solaris/amd64 seems to have wildly different - // ticks on different CPUs. - return ErrTimeOrder - } - case EvTimerGoroutine: - p.timerGoids[raw.args[0]] = true - case EvStack: - if len(raw.args) < 2 { - return fmt.Errorf("EvStack has wrong number of arguments: want at least 2, got %d", len(raw.args)) - } - size := raw.args[1] - if size > 1000 { - return fmt.Errorf("EvStack has bad number of frames: %d", size) - } - want := 2 + 4*size - if uint64(len(raw.args)) != want { - return fmt.Errorf("EvStack has wrong number of arguments: want %d, got %d", want, len(raw.args)) - } - id := uint32(raw.args[0]) - if id != 0 && size > 0 { - stk := p.allocateStack(size) - for i := 0; i < int(size); i++ { - pc := raw.args[2+i*4+0] - fn := raw.args[2+i*4+1] - file := raw.args[2+i*4+2] - line := raw.args[2+i*4+3] - stk[i] = pc - - if _, ok := p.pcs[pc]; !ok { - p.pcs[pc] = Frame{PC: pc, Fn: fn, File: file, Line: int(line)} - } - } - p.stacks[id] = stk - } - case EvCPUSample: - // These events get parsed during the indexing step and don't strictly - // belong to the batch. - default: - *ev = Event{Type: raw.typ, P: p.lastP, G: p.lastG} - var argOffset int - ev.Ts = p.lastTs + Timestamp(raw.args[0]) - argOffset = 1 - p.lastTs = ev.Ts - for i := argOffset; i < narg; i++ { - if i == narg-1 && desc.Stack { - ev.StkID = uint32(raw.args[i]) - } else { - ev.Args[i-argOffset] = raw.args[i] - } - } - switch raw.typ { - case EvGoStart, EvGoStartLocal, EvGoStartLabel: - p.lastG = ev.Args[0] - ev.G = p.lastG - case EvGoEnd, EvGoStop, EvGoSched, EvGoPreempt, - EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv, - EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, - EvGoSysBlock, EvGoBlockGC: - p.lastG = 0 - case EvGoSysExit, EvGoWaiting, EvGoInSyscall: - ev.G = ev.Args[0] - case EvUserTaskCreate: - // e.Args 0: taskID, 1:parentID, 2:nameID - case EvUserRegion: - // e.Args 0: taskID, 1: mode, 2:nameID - case EvUserLog: - // e.Args 0: taskID, 1:keyID, 2: stackID, 3: messageID - // raw.sargs 0: message - - if id, ok := p.inlineStringsMapping[raw.sargs[0]]; ok { - ev.Args[3] = uint64(id) - } else { - id := len(p.inlineStrings) - p.inlineStringsMapping[raw.sargs[0]] = id - p.inlineStrings = append(p.inlineStrings, raw.sargs[0]) - ev.Args[3] = uint64(id) - } - } - - return nil - } - - ev.Type = EvNone - return nil -} - -// ErrTimeOrder is returned by Parse when the trace contains -// time stamps that do not respect actual event ordering. -var ErrTimeOrder = errors.New("time stamps out of order") - -// postProcessTrace does inter-event verification and information restoration. -// The resulting trace is guaranteed to be consistent -// (for example, a P does not run two Gs at the same time, or a G is indeed -// blocked before an unblock event). -func (p *parser) postProcessTrace(events Events) error { - const ( - gDead = iota - gRunnable - gRunning - gWaiting - ) - type gdesc struct { - state int - ev *Event - evStart *Event - evCreate *Event - evMarkAssist *Event - } - type pdesc struct { - running bool - g uint64 - evSweep *Event - } - - gs := make(map[uint64]gdesc) - ps := make(map[int32]pdesc) - tasks := make(map[uint64]*Event) // task id to task creation events - activeRegions := make(map[uint64][]*Event) // goroutine id to stack of regions - gs[0] = gdesc{state: gRunning} - var evGC, evSTW *Event - - checkRunning := func(p pdesc, g gdesc, ev *Event, allowG0 bool) error { - name := EventDescriptions[ev.Type].Name - if g.state != gRunning { - return fmt.Errorf("g %d is not running while %s (time %d)", ev.G, name, ev.Ts) - } - if p.g != ev.G { - return fmt.Errorf("p %d is not running g %d while %s (time %d)", ev.P, ev.G, name, ev.Ts) - } - if !allowG0 && ev.G == 0 { - return fmt.Errorf("g 0 did %s (time %d)", name, ev.Ts) - } - return nil - } - - for evIdx := 0; evIdx < events.Len(); evIdx++ { - ev := events.Ptr(evIdx) - - switch ev.Type { - case EvProcStart: - p := ps[ev.P] - if p.running { - return fmt.Errorf("p %d is running before start (time %d)", ev.P, ev.Ts) - } - p.running = true - - ps[ev.P] = p - case EvProcStop: - p := ps[ev.P] - if !p.running { - return fmt.Errorf("p %d is not running before stop (time %d)", ev.P, ev.Ts) - } - if p.g != 0 { - return fmt.Errorf("p %d is running a goroutine %d during stop (time %d)", ev.P, p.g, ev.Ts) - } - p.running = false - - ps[ev.P] = p - case EvGCStart: - if evGC != nil { - return fmt.Errorf("previous GC is not ended before a new one (time %d)", ev.Ts) - } - evGC = ev - // Attribute this to the global GC state. - ev.P = GCP - case EvGCDone: - if evGC == nil { - return fmt.Errorf("bogus GC end (time %d)", ev.Ts) - } - evGC = nil - case EvSTWStart: - evp := &evSTW - if *evp != nil { - return fmt.Errorf("previous STW is not ended before a new one (time %d)", ev.Ts) - } - *evp = ev - case EvSTWDone: - evp := &evSTW - if *evp == nil { - return fmt.Errorf("bogus STW end (time %d)", ev.Ts) - } - *evp = nil - case EvGCSweepStart: - p := ps[ev.P] - if p.evSweep != nil { - return fmt.Errorf("previous sweeping is not ended before a new one (time %d)", ev.Ts) - } - p.evSweep = ev - - ps[ev.P] = p - case EvGCMarkAssistStart: - g := gs[ev.G] - if g.evMarkAssist != nil { - return fmt.Errorf("previous mark assist is not ended before a new one (time %d)", ev.Ts) - } - g.evMarkAssist = ev - - gs[ev.G] = g - case EvGCMarkAssistDone: - // Unlike most events, mark assists can be in progress when a - // goroutine starts tracing, so we can't report an error here. - g := gs[ev.G] - if g.evMarkAssist != nil { - g.evMarkAssist = nil - } - - gs[ev.G] = g - case EvGCSweepDone: - p := ps[ev.P] - if p.evSweep == nil { - return fmt.Errorf("bogus sweeping end (time %d)", ev.Ts) - } - p.evSweep = nil - - ps[ev.P] = p - case EvGoWaiting: - g := gs[ev.G] - if g.state != gRunnable { - return fmt.Errorf("g %d is not runnable before EvGoWaiting (time %d)", ev.G, ev.Ts) - } - g.state = gWaiting - g.ev = ev - - gs[ev.G] = g - case EvGoInSyscall: - g := gs[ev.G] - if g.state != gRunnable { - return fmt.Errorf("g %d is not runnable before EvGoInSyscall (time %d)", ev.G, ev.Ts) - } - g.state = gWaiting - g.ev = ev - - gs[ev.G] = g - case EvGoCreate: - g := gs[ev.G] - p := ps[ev.P] - if err := checkRunning(p, g, ev, true); err != nil { - return err - } - if _, ok := gs[ev.Args[0]]; ok { - return fmt.Errorf("g %d already exists (time %d)", ev.Args[0], ev.Ts) - } - gs[ev.Args[0]] = gdesc{state: gRunnable, ev: ev, evCreate: ev} - - case EvGoStart, EvGoStartLabel: - g := gs[ev.G] - p := ps[ev.P] - if g.state != gRunnable { - return fmt.Errorf("g %d is not runnable before start (time %d)", ev.G, ev.Ts) - } - if p.g != 0 { - return fmt.Errorf("p %d is already running g %d while start g %d (time %d)", ev.P, p.g, ev.G, ev.Ts) - } - g.state = gRunning - g.evStart = ev - p.g = ev.G - if g.evCreate != nil { - ev.StkID = uint32(g.evCreate.Args[1]) - g.evCreate = nil - } - - if g.ev != nil { - g.ev = nil - } - - gs[ev.G] = g - ps[ev.P] = p - case EvGoEnd, EvGoStop: - g := gs[ev.G] - p := ps[ev.P] - if err := checkRunning(p, g, ev, false); err != nil { - return err - } - g.evStart = nil - g.state = gDead - p.g = 0 - - if ev.Type == EvGoEnd { // flush all active regions - delete(activeRegions, ev.G) - } - - gs[ev.G] = g - ps[ev.P] = p - case EvGoSched, EvGoPreempt: - g := gs[ev.G] - p := ps[ev.P] - if err := checkRunning(p, g, ev, false); err != nil { - return err - } - g.state = gRunnable - g.evStart = nil - p.g = 0 - g.ev = ev - - gs[ev.G] = g - ps[ev.P] = p - case EvGoUnblock: - g := gs[ev.G] - p := ps[ev.P] - if g.state != gRunning { - return fmt.Errorf("g %d is not running while unpark (time %d)", ev.G, ev.Ts) - } - if ev.P != TimerP && p.g != ev.G { - return fmt.Errorf("p %d is not running g %d while unpark (time %d)", ev.P, ev.G, ev.Ts) - } - g1 := gs[ev.Args[0]] - if g1.state != gWaiting { - return fmt.Errorf("g %d is not waiting before unpark (time %d)", ev.Args[0], ev.Ts) - } - if g1.ev != nil && g1.ev.Type == EvGoBlockNet { - ev.P = NetpollP - } - g1.state = gRunnable - g1.ev = ev - gs[ev.Args[0]] = g1 - - case EvGoSysCall: - g := gs[ev.G] - p := ps[ev.P] - if err := checkRunning(p, g, ev, false); err != nil { - return err - } - g.ev = ev - - gs[ev.G] = g - case EvGoSysBlock: - g := gs[ev.G] - p := ps[ev.P] - if err := checkRunning(p, g, ev, false); err != nil { - return err - } - g.state = gWaiting - g.evStart = nil - p.g = 0 - - gs[ev.G] = g - ps[ev.P] = p - case EvGoSysExit: - g := gs[ev.G] - if g.state != gWaiting { - return fmt.Errorf("g %d is not waiting during syscall exit (time %d)", ev.G, ev.Ts) - } - g.state = gRunnable - g.ev = ev - - gs[ev.G] = g - case EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv, - EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoBlockGC: - g := gs[ev.G] - p := ps[ev.P] - if err := checkRunning(p, g, ev, false); err != nil { - return err - } - g.state = gWaiting - g.ev = ev - g.evStart = nil - p.g = 0 - - gs[ev.G] = g - ps[ev.P] = p - case EvUserTaskCreate: - taskid := ev.Args[0] - if prevEv, ok := tasks[taskid]; ok { - return fmt.Errorf("task id conflicts (id:%d), %q vs %q", taskid, ev, prevEv) - } - tasks[ev.Args[0]] = ev - - case EvUserTaskEnd: - taskid := ev.Args[0] - delete(tasks, taskid) - - case EvUserRegion: - mode := ev.Args[1] - regions := activeRegions[ev.G] - if mode == 0 { // region start - activeRegions[ev.G] = append(regions, ev) // push - } else if mode == 1 { // region end - n := len(regions) - if n > 0 { // matching region start event is in the trace. - s := regions[n-1] - if s.Args[0] != ev.Args[0] || s.Args[2] != ev.Args[2] { // task id, region name mismatch - return fmt.Errorf("misuse of region in goroutine %d: span end %q when the inner-most active span start event is %q", ev.G, ev, s) - } - - if n > 1 { - activeRegions[ev.G] = regions[:n-1] - } else { - delete(activeRegions, ev.G) - } - } - } else { - return fmt.Errorf("invalid user region mode: %q", ev) - } - } - - if ev.StkID != 0 && len(p.stacks[ev.StkID]) == 0 { - // Make sure events don't refer to stacks that don't exist or to - // stacks with zero frames. Neither of these should be possible, but - // better be safe than sorry. - - ev.StkID = 0 - } - - } - - // TODO(mknyszek): restore stacks for EvGoStart events. - return nil -} - -var errMalformedVarint = errors.New("malformatted base-128 varint") - -// readVal reads unsigned base-128 value from r. -func (p *parser) readVal() (uint64, error) { - v, n := binary.Uvarint(p.data[p.off:]) - if n <= 0 { - return 0, errMalformedVarint - } - p.off += n - return v, nil -} - -func readValFrom(buf []byte) (v uint64, rem []byte, err error) { - v, n := binary.Uvarint(buf) - if n <= 0 { - return 0, nil, errMalformedVarint - } - return v, buf[n:], nil -} - -func (ev *Event) String() string { - desc := &EventDescriptions[ev.Type] - w := new(bytes.Buffer) - fmt.Fprintf(w, "%d %s p=%d g=%d stk=%d", ev.Ts, desc.Name, ev.P, ev.G, ev.StkID) - for i, a := range desc.Args { - fmt.Fprintf(w, " %s=%d", a, ev.Args[i]) - } - return w.String() -} - -// argNum returns total number of args for the event accounting for timestamps, -// sequence numbers and differences between trace format versions. -func (raw *rawEvent) argNum() int { - desc := &EventDescriptions[raw.typ] - if raw.typ == EvStack { - return len(raw.args) - } - narg := len(desc.Args) - if desc.Stack { - narg++ - } - switch raw.typ { - case EvBatch, EvFrequency, EvTimerGoroutine: - return narg - } - narg++ // timestamp - return narg -} - -// Event types in the trace. -// Verbatim copy from src/runtime/trace.go with the "trace" prefix removed. -const ( - EvNone event.Type = 0 // unused - EvBatch event.Type = 1 // start of per-P batch of events [pid, timestamp] - EvFrequency event.Type = 2 // contains tracer timer frequency [frequency (ticks per second)] - EvStack event.Type = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] - EvGomaxprocs event.Type = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] - EvProcStart event.Type = 5 // start of P [timestamp, thread id] - EvProcStop event.Type = 6 // stop of P [timestamp] - EvGCStart event.Type = 7 // GC start [timestamp, seq, stack id] - EvGCDone event.Type = 8 // GC done [timestamp] - EvSTWStart event.Type = 9 // GC mark termination start [timestamp, kind] - EvSTWDone event.Type = 10 // GC mark termination done [timestamp] - EvGCSweepStart event.Type = 11 // GC sweep start [timestamp, stack id] - EvGCSweepDone event.Type = 12 // GC sweep done [timestamp, swept, reclaimed] - EvGoCreate event.Type = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id] - EvGoStart event.Type = 14 // goroutine starts running [timestamp, goroutine id, seq] - EvGoEnd event.Type = 15 // goroutine ends [timestamp] - EvGoStop event.Type = 16 // goroutine stops (like in select{}) [timestamp, stack] - EvGoSched event.Type = 17 // goroutine calls Gosched [timestamp, stack] - EvGoPreempt event.Type = 18 // goroutine is preempted [timestamp, stack] - EvGoSleep event.Type = 19 // goroutine calls Sleep [timestamp, stack] - EvGoBlock event.Type = 20 // goroutine blocks [timestamp, stack] - EvGoUnblock event.Type = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack] - EvGoBlockSend event.Type = 22 // goroutine blocks on chan send [timestamp, stack] - EvGoBlockRecv event.Type = 23 // goroutine blocks on chan recv [timestamp, stack] - EvGoBlockSelect event.Type = 24 // goroutine blocks on select [timestamp, stack] - EvGoBlockSync event.Type = 25 // goroutine blocks on Mutex/RWMutex [timestamp, stack] - EvGoBlockCond event.Type = 26 // goroutine blocks on Cond [timestamp, stack] - EvGoBlockNet event.Type = 27 // goroutine blocks on network [timestamp, stack] - EvGoSysCall event.Type = 28 // syscall enter [timestamp, stack] - EvGoSysExit event.Type = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp] - EvGoSysBlock event.Type = 30 // syscall blocks [timestamp] - EvGoWaiting event.Type = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id] - EvGoInSyscall event.Type = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id] - EvHeapAlloc event.Type = 33 // gcController.heapLive change [timestamp, heap live bytes] - EvHeapGoal event.Type = 34 // gcController.heapGoal change [timestamp, heap goal bytes] - EvTimerGoroutine event.Type = 35 // denotes timer goroutine [timer goroutine id] - EvFutileWakeup event.Type = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] - EvString event.Type = 37 // string dictionary entry [ID, length, string] - EvGoStartLocal event.Type = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id] - EvGoUnblockLocal event.Type = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack] - EvGoSysExitLocal event.Type = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp] - EvGoStartLabel event.Type = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id] - EvGoBlockGC event.Type = 42 // goroutine blocks on GC assist [timestamp, stack] - EvGCMarkAssistStart event.Type = 43 // GC mark assist start [timestamp, stack] - EvGCMarkAssistDone event.Type = 44 // GC mark assist done [timestamp] - EvUserTaskCreate event.Type = 45 // trace.NewTask [timestamp, internal task id, internal parent id, stack, name string] - EvUserTaskEnd event.Type = 46 // end of task [timestamp, internal task id, stack] - EvUserRegion event.Type = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), name string] - EvUserLog event.Type = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] - EvCPUSample event.Type = 49 // CPU profiling sample [timestamp, stack, real timestamp, real P id (-1 when absent), goroutine id] - EvCount event.Type = 50 -) - -var EventDescriptions = [256]struct { - Name string - minVersion version.Version - Stack bool - Args []string - SArgs []string // string arguments -}{ - EvNone: {"None", 5, false, []string{}, nil}, - EvBatch: {"Batch", 5, false, []string{"p", "ticks"}, nil}, // in 1.5 format it was {"p", "seq", "ticks"} - EvFrequency: {"Frequency", 5, false, []string{"freq"}, nil}, // in 1.5 format it was {"freq", "unused"} - EvStack: {"Stack", 5, false, []string{"id", "siz"}, nil}, - EvGomaxprocs: {"Gomaxprocs", 5, true, []string{"procs"}, nil}, - EvProcStart: {"ProcStart", 5, false, []string{"thread"}, nil}, - EvProcStop: {"ProcStop", 5, false, []string{}, nil}, - EvGCStart: {"GCStart", 5, true, []string{"seq"}, nil}, // in 1.5 format it was {} - EvGCDone: {"GCDone", 5, false, []string{}, nil}, - EvSTWStart: {"GCSTWStart", 5, false, []string{"kindid"}, []string{"kind"}}, // <= 1.9, args was {} (implicitly {0}) - EvSTWDone: {"GCSTWDone", 5, false, []string{}, nil}, - EvGCSweepStart: {"GCSweepStart", 5, true, []string{}, nil}, - EvGCSweepDone: {"GCSweepDone", 5, false, []string{"swept", "reclaimed"}, nil}, // before 1.9, format was {} - EvGoCreate: {"GoCreate", 5, true, []string{"g", "stack"}, nil}, - EvGoStart: {"GoStart", 5, false, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} - EvGoEnd: {"GoEnd", 5, false, []string{}, nil}, - EvGoStop: {"GoStop", 5, true, []string{}, nil}, - EvGoSched: {"GoSched", 5, true, []string{}, nil}, - EvGoPreempt: {"GoPreempt", 5, true, []string{}, nil}, - EvGoSleep: {"GoSleep", 5, true, []string{}, nil}, - EvGoBlock: {"GoBlock", 5, true, []string{}, nil}, - EvGoUnblock: {"GoUnblock", 5, true, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} - EvGoBlockSend: {"GoBlockSend", 5, true, []string{}, nil}, - EvGoBlockRecv: {"GoBlockRecv", 5, true, []string{}, nil}, - EvGoBlockSelect: {"GoBlockSelect", 5, true, []string{}, nil}, - EvGoBlockSync: {"GoBlockSync", 5, true, []string{}, nil}, - EvGoBlockCond: {"GoBlockCond", 5, true, []string{}, nil}, - EvGoBlockNet: {"GoBlockNet", 5, true, []string{}, nil}, - EvGoSysCall: {"GoSysCall", 5, true, []string{}, nil}, - EvGoSysExit: {"GoSysExit", 5, false, []string{"g", "seq", "ts"}, nil}, - EvGoSysBlock: {"GoSysBlock", 5, false, []string{}, nil}, - EvGoWaiting: {"GoWaiting", 5, false, []string{"g"}, nil}, - EvGoInSyscall: {"GoInSyscall", 5, false, []string{"g"}, nil}, - EvHeapAlloc: {"HeapAlloc", 5, false, []string{"mem"}, nil}, - EvHeapGoal: {"HeapGoal", 5, false, []string{"mem"}, nil}, - EvTimerGoroutine: {"TimerGoroutine", 5, false, []string{"g"}, nil}, // in 1.5 format it was {"g", "unused"} - EvFutileWakeup: {"FutileWakeup", 5, false, []string{}, nil}, - EvString: {"String", 7, false, []string{}, nil}, - EvGoStartLocal: {"GoStartLocal", 7, false, []string{"g"}, nil}, - EvGoUnblockLocal: {"GoUnblockLocal", 7, true, []string{"g"}, nil}, - EvGoSysExitLocal: {"GoSysExitLocal", 7, false, []string{"g", "ts"}, nil}, - EvGoStartLabel: {"GoStartLabel", 8, false, []string{"g", "seq", "labelid"}, []string{"label"}}, - EvGoBlockGC: {"GoBlockGC", 8, true, []string{}, nil}, - EvGCMarkAssistStart: {"GCMarkAssistStart", 9, true, []string{}, nil}, - EvGCMarkAssistDone: {"GCMarkAssistDone", 9, false, []string{}, nil}, - EvUserTaskCreate: {"UserTaskCreate", 11, true, []string{"taskid", "pid", "typeid"}, []string{"name"}}, - EvUserTaskEnd: {"UserTaskEnd", 11, true, []string{"taskid"}, nil}, - EvUserRegion: {"UserRegion", 11, true, []string{"taskid", "mode", "typeid"}, []string{"name"}}, - EvUserLog: {"UserLog", 11, true, []string{"id", "keyid"}, []string{"category", "message"}}, - EvCPUSample: {"CPUSample", 19, true, []string{"ts", "p", "g"}, nil}, -} - -//gcassert:inline -func (p *parser) allocateStack(size uint64) []uint64 { - if size == 0 { - return nil - } - - // Stacks are plentiful but small. For our "Staticcheck on std" trace with - // 11e6 events, we have roughly 500,000 stacks, using 200 MiB of memory. To - // avoid making 500,000 small allocations we allocate backing arrays 1 MiB - // at a time. - out := p.stacksData - if uint64(len(out)) < size { - out = make([]uint64, 1024*128) - } - p.stacksData = out[size:] - return out[:size:size] -} - -func (tr *Trace) STWReason(kindID uint64) STWReason { - if tr.Version < 21 { - if kindID == 0 || kindID == 1 { - return STWReason(kindID + 1) - } else { - return STWUnknown - } - } else if tr.Version == 21 { - if kindID < NumSTWReasons { - return STWReason(kindID) - } else { - return STWUnknown - } - } else { - return STWUnknown - } -} - -type STWReason int - -const ( - STWUnknown STWReason = 0 - STWGCMarkTermination STWReason = 1 - STWGCSweepTermination STWReason = 2 - STWWriteHeapDump STWReason = 3 - STWGoroutineProfile STWReason = 4 - STWGoroutineProfileCleanup STWReason = 5 - STWAllGoroutinesStackTrace STWReason = 6 - STWReadMemStats STWReason = 7 - STWAllThreadsSyscall STWReason = 8 - STWGOMAXPROCS STWReason = 9 - STWStartTrace STWReason = 10 - STWStopTrace STWReason = 11 - STWCountPagesInUse STWReason = 12 - STWReadMetricsSlow STWReason = 13 - STWReadMemStatsSlow STWReason = 14 - STWPageCachePagesLeaked STWReason = 15 - STWResetDebugLog STWReason = 16 - - NumSTWReasons = 17 -) diff --git a/src/internal/trace/v2/internal/oldtrace/parser_test.go b/src/internal/trace/v2/internal/oldtrace/parser_test.go deleted file mode 100644 index bc26c36a44..0000000000 --- a/src/internal/trace/v2/internal/oldtrace/parser_test.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015 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 oldtrace - -import ( - "bytes" - "internal/trace/v2/version" - "os" - "path/filepath" - "strings" - "testing" -) - -func TestCorruptedInputs(t *testing.T) { - // These inputs crashed parser previously. - tests := []string{ - "gotrace\x00\x020", - "gotrace\x00Q00\x020", - "gotrace\x00T00\x020", - "gotrace\x00\xc3\x0200", - "go 1.5 trace\x00\x00\x00\x00\x020", - "go 1.5 trace\x00\x00\x00\x00Q00\x020", - "go 1.5 trace\x00\x00\x00\x00T00\x020", - "go 1.5 trace\x00\x00\x00\x00\xc3\x0200", - } - for _, data := range tests { - res, err := Parse(strings.NewReader(data), 5) - if err == nil || res.Events.Len() != 0 || res.Stacks != nil { - t.Fatalf("no error on input: %q", data) - } - } -} - -func TestParseCanned(t *testing.T) { - files, err := os.ReadDir("./testdata") - if err != nil { - t.Fatalf("failed to read ./testdata: %v", err) - } - for _, f := range files { - info, err := f.Info() - if err != nil { - t.Fatal(err) - } - if testing.Short() && info.Size() > 10000 { - continue - } - name := filepath.Join("./testdata", f.Name()) - data, err := os.ReadFile(name) - if err != nil { - t.Fatal(err) - } - r := bytes.NewReader(data) - v, err := version.ReadHeader(r) - if err != nil { - t.Errorf("failed to parse good trace %s: %s", f.Name(), err) - } - trace, err := Parse(r, v) - switch { - case strings.HasSuffix(f.Name(), "_good"): - if err != nil { - t.Errorf("failed to parse good trace %v: %v", f.Name(), err) - } - checkTrace(t, int(v), trace) - case strings.HasSuffix(f.Name(), "_unordered"): - if err != ErrTimeOrder { - t.Errorf("unordered trace is not detected %v: %v", f.Name(), err) - } - default: - t.Errorf("unknown input file suffix: %v", f.Name()) - } - } -} - -// checkTrace walks over a good trace and makes a bunch of additional checks -// that may not cause the parser to outright fail. -func checkTrace(t *testing.T, ver int, res Trace) { - for i := 0; i < res.Events.Len(); i++ { - ev := res.Events.Ptr(i) - if ver >= 21 { - if ev.Type == EvSTWStart && res.Strings[ev.Args[0]] == "unknown" { - t.Errorf("found unknown STW event; update stwReasonStrings?") - } - } - } -} - -func TestBuckets(t *testing.T) { - var evs Events - - const N = eventsBucketSize*3 + 123 - for i := 0; i < N; i++ { - evs.append(Event{Ts: Timestamp(i)}) - } - - if n := len(evs.buckets); n != 4 { - t.Fatalf("got %d buckets, want %d", n, 4) - } - - if n := evs.Len(); n != N { - t.Fatalf("got %d events, want %d", n, N) - } - - var n int - evs.All()(func(ev *Event) bool { - n++ - return true - }) - if n != N { - t.Fatalf("iterated over %d events, expected %d", n, N) - } - - const consume = eventsBucketSize + 50 - for i := 0; i < consume; i++ { - if _, ok := evs.Pop(); !ok { - t.Fatalf("iteration failed after %d events", i) - } - } - - if evs.buckets[0] != nil { - t.Fatalf("expected first bucket to have been dropped") - } - for i, b := range evs.buckets[1:] { - if b == nil { - t.Fatalf("expected bucket %d to be non-nil", i+1) - } - } - - if n := evs.Len(); n != N-consume { - t.Fatalf("got %d remaining elements, expected %d", n, N-consume) - } - - ev := evs.Ptr(0) - if ev.Ts != consume { - t.Fatalf("got event %d, expected %d", int(ev.Ts), consume) - } - - for { - _, ok := evs.Pop() - if !ok { - break - } - } - - for i, b := range evs.buckets { - if b != nil { - t.Fatalf("expected bucket %d to be nil", i) - } - } -} diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/fmt_1_21_pprof_good b/src/internal/trace/v2/internal/oldtrace/testdata/fmt_1_21_pprof_good deleted file mode 100644 index f3ea402fdb..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/fmt_1_21_pprof_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/http_1_19_good b/src/internal/trace/v2/internal/oldtrace/testdata/http_1_19_good deleted file mode 100644 index c1d519e7d5..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/http_1_19_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/http_1_21_good b/src/internal/trace/v2/internal/oldtrace/testdata/http_1_21_good deleted file mode 100644 index b3295f9e5d..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/http_1_21_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_11_good b/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_11_good deleted file mode 100644 index 6468d89290..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_11_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_19_good b/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_19_good deleted file mode 100644 index 13f59268e6..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_19_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_21_good b/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_21_good deleted file mode 100644 index 1ade5e0eb6..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/stress_1_21_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_11_good b/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_11_good deleted file mode 100644 index 457f01a6cd..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_11_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_19_good b/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_19_good deleted file mode 100644 index 92d92789c4..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_19_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_21_good b/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_21_good deleted file mode 100644 index fff46a9a07..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/stress_start_stop_1_21_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_11_good b/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_11_good deleted file mode 100644 index f4edb67e65..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_11_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_19_good b/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_19_good deleted file mode 100644 index 1daa3b25a6..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_19_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_21_good b/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_21_good deleted file mode 100644 index 5c01a6405d..0000000000 Binary files a/src/internal/trace/v2/internal/oldtrace/testdata/user_task_region_1_21_good and /dev/null differ diff --git a/src/internal/trace/v2/internal/testgen/go122/trace.go b/src/internal/trace/v2/internal/testgen/go122/trace.go deleted file mode 100644 index 42bb403482..0000000000 --- a/src/internal/trace/v2/internal/testgen/go122/trace.go +++ /dev/null @@ -1,401 +0,0 @@ -// 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. - -package testkit - -import ( - "bytes" - "encoding/binary" - "fmt" - "os" - "regexp" - "strings" - - "internal/trace/v2" - "internal/trace/v2/event" - "internal/trace/v2/event/go122" - "internal/trace/v2/raw" - "internal/trace/v2/version" - "internal/txtar" -) - -func Main(f func(*Trace)) { - // Create an output file. - out, err := os.Create(os.Args[1]) - if err != nil { - panic(err.Error()) - } - defer out.Close() - - // Create a new trace. - trace := NewTrace() - - // Call the generator. - f(trace) - - // Write out the generator's state. - if _, err := out.Write(trace.Generate()); err != nil { - panic(err.Error()) - } -} - -// Trace represents an execution trace for testing. -// -// It does a little bit of work to ensure that the produced trace is valid, -// just for convenience. It mainly tracks batches and batch sizes (so they're -// trivially correct), tracks strings and stacks, and makes sure emitted string -// and stack batches are valid. That last part can be controlled by a few options. -// -// Otherwise, it performs no validation on the trace at all. -type Trace struct { - // Trace data state. - ver version.Version - names map[string]event.Type - specs []event.Spec - events []raw.Event - gens []*Generation - validTimestamps bool - - // Expectation state. - bad bool - badMatch *regexp.Regexp -} - -// NewTrace creates a new trace. -func NewTrace() *Trace { - ver := version.Go122 - return &Trace{ - names: event.Names(ver.Specs()), - specs: ver.Specs(), - validTimestamps: true, - } -} - -// ExpectFailure writes down that the trace should be broken. The caller -// must provide a pattern matching the expected error produced by the parser. -func (t *Trace) ExpectFailure(pattern string) { - t.bad = true - t.badMatch = regexp.MustCompile(pattern) -} - -// ExpectSuccess writes down that the trace should successfully parse. -func (t *Trace) ExpectSuccess() { - t.bad = false -} - -// RawEvent emits an event into the trace. name must correspond to one -// of the names in Specs() result for the version that was passed to -// this trace. -func (t *Trace) RawEvent(typ event.Type, data []byte, args ...uint64) { - t.events = append(t.events, t.createEvent(typ, data, args...)) -} - -// DisableTimestamps makes the timestamps for all events generated after -// this call zero. Raw events are exempted from this because the caller -// has to pass their own timestamp into those events anyway. -func (t *Trace) DisableTimestamps() { - t.validTimestamps = false -} - -// Generation creates a new trace generation. -// -// This provides more structure than Event to allow for more easily -// creating complex traces that are mostly or completely correct. -func (t *Trace) Generation(gen uint64) *Generation { - g := &Generation{ - trace: t, - gen: gen, - strings: make(map[string]uint64), - stacks: make(map[stack]uint64), - } - t.gens = append(t.gens, g) - return g -} - -// Generate creates a test file for the trace. -func (t *Trace) Generate() []byte { - // Trace file contents. - var buf bytes.Buffer - tw, err := raw.NewTextWriter(&buf, version.Go122) - if err != nil { - panic(err.Error()) - } - - // Write raw top-level events. - for _, e := range t.events { - tw.WriteEvent(e) - } - - // Write generations. - for _, g := range t.gens { - g.writeEventsTo(tw) - } - - // Expectation file contents. - expect := []byte("SUCCESS\n") - if t.bad { - expect = []byte(fmt.Sprintf("FAILURE %q\n", t.badMatch)) - } - - // Create the test file's contents. - return txtar.Format(&txtar.Archive{ - Files: []txtar.File{ - {Name: "expect", Data: expect}, - {Name: "trace", Data: buf.Bytes()}, - }, - }) -} - -func (t *Trace) createEvent(ev event.Type, data []byte, args ...uint64) raw.Event { - spec := t.specs[ev] - if ev != go122.EvStack { - if arity := len(spec.Args); len(args) != arity { - panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args))) - } - } - return raw.Event{ - Version: version.Go122, - Ev: ev, - Args: args, - Data: data, - } -} - -type stack struct { - stk [32]trace.StackFrame - len int -} - -var ( - NoString = "" - NoStack = []trace.StackFrame{} -) - -// Generation represents a single generation in the trace. -type Generation struct { - trace *Trace - gen uint64 - batches []*Batch - strings map[string]uint64 - stacks map[stack]uint64 - - // Options applied when Trace.Generate is called. - ignoreStringBatchSizeLimit bool - ignoreStackBatchSizeLimit bool -} - -// Batch starts a new event batch in the trace data. -// -// This is convenience function for generating correct batches. -func (g *Generation) Batch(thread trace.ThreadID, time Time) *Batch { - if !g.trace.validTimestamps { - time = 0 - } - b := &Batch{ - gen: g, - thread: thread, - timestamp: time, - } - g.batches = append(g.batches, b) - return b -} - -// String registers a string with the trace. -// -// This is a convenience function for easily adding correct -// strings to traces. -func (g *Generation) String(s string) uint64 { - if len(s) == 0 { - return 0 - } - if id, ok := g.strings[s]; ok { - return id - } - id := uint64(len(g.strings) + 1) - g.strings[s] = id - return id -} - -// Stack registers a stack with the trace. -// -// This is a convenience function for easily adding correct -// stacks to traces. -func (g *Generation) Stack(stk []trace.StackFrame) uint64 { - if len(stk) == 0 { - return 0 - } - if len(stk) > 32 { - panic("stack too big for test") - } - var stkc stack - copy(stkc.stk[:], stk) - stkc.len = len(stk) - if id, ok := g.stacks[stkc]; ok { - return id - } - id := uint64(len(g.stacks) + 1) - g.stacks[stkc] = id - return id -} - -// writeEventsTo emits event batches in the generation to tw. -func (g *Generation) writeEventsTo(tw *raw.TextWriter) { - // Write event batches for the generation. - for _, b := range g.batches { - b.writeEventsTo(tw) - } - - // Write frequency. - b := g.newStructuralBatch() - b.RawEvent(go122.EvFrequency, nil, 15625000) - b.writeEventsTo(tw) - - // Write stacks. - b = g.newStructuralBatch() - b.RawEvent(go122.EvStacks, nil) - for stk, id := range g.stacks { - stk := stk.stk[:stk.len] - args := []uint64{id} - for _, f := range stk { - args = append(args, f.PC, g.String(f.Func), g.String(f.File), f.Line) - } - b.RawEvent(go122.EvStack, nil, args...) - - // Flush the batch if necessary. - if !g.ignoreStackBatchSizeLimit && b.size > go122.MaxBatchSize/2 { - b.writeEventsTo(tw) - b = g.newStructuralBatch() - } - } - b.writeEventsTo(tw) - - // Write strings. - b = g.newStructuralBatch() - b.RawEvent(go122.EvStrings, nil) - for s, id := range g.strings { - b.RawEvent(go122.EvString, []byte(s), id) - - // Flush the batch if necessary. - if !g.ignoreStringBatchSizeLimit && b.size > go122.MaxBatchSize/2 { - b.writeEventsTo(tw) - b = g.newStructuralBatch() - } - } - b.writeEventsTo(tw) -} - -func (g *Generation) newStructuralBatch() *Batch { - return &Batch{gen: g, thread: trace.NoThread} -} - -// Batch represents an event batch. -type Batch struct { - gen *Generation - thread trace.ThreadID - timestamp Time - size uint64 - events []raw.Event -} - -// Event emits an event into a batch. name must correspond to one -// of the names in Specs() result for the version that was passed to -// this trace. Callers must omit the timestamp delta. -func (b *Batch) Event(name string, args ...any) { - ev, ok := b.gen.trace.names[name] - if !ok { - panic(fmt.Sprintf("invalid or unknown event %s", name)) - } - var uintArgs []uint64 - argOff := 0 - if b.gen.trace.specs[ev].IsTimedEvent { - if b.gen.trace.validTimestamps { - uintArgs = []uint64{1} - } else { - uintArgs = []uint64{0} - } - argOff = 1 - } - spec := b.gen.trace.specs[ev] - if arity := len(spec.Args) - argOff; len(args) != arity { - panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args))) - } - for i, arg := range args { - uintArgs = append(uintArgs, b.uintArgFor(arg, spec.Args[i+argOff])) - } - b.RawEvent(ev, nil, uintArgs...) -} - -func (b *Batch) uintArgFor(arg any, argSpec string) uint64 { - components := strings.SplitN(argSpec, "_", 2) - typStr := components[0] - if len(components) == 2 { - typStr = components[1] - } - var u uint64 - switch typStr { - case "value": - u = arg.(uint64) - case "stack": - u = b.gen.Stack(arg.([]trace.StackFrame)) - case "seq": - u = uint64(arg.(Seq)) - case "pstatus": - u = uint64(arg.(go122.ProcStatus)) - case "gstatus": - u = uint64(arg.(go122.GoStatus)) - case "g": - u = uint64(arg.(trace.GoID)) - case "m": - u = uint64(arg.(trace.ThreadID)) - case "p": - u = uint64(arg.(trace.ProcID)) - case "string": - u = b.gen.String(arg.(string)) - case "task": - u = uint64(arg.(trace.TaskID)) - default: - panic(fmt.Sprintf("unsupported arg type %q for spec %q", typStr, argSpec)) - } - return u -} - -// RawEvent emits an event into a batch. name must correspond to one -// of the names in Specs() result for the version that was passed to -// this trace. -func (b *Batch) RawEvent(typ event.Type, data []byte, args ...uint64) { - ev := b.gen.trace.createEvent(typ, data, args...) - - // Compute the size of the event and add it to the batch. - b.size += 1 // One byte for the event header. - var buf [binary.MaxVarintLen64]byte - for _, arg := range args { - b.size += uint64(binary.PutUvarint(buf[:], arg)) - } - if len(data) != 0 { - b.size += uint64(binary.PutUvarint(buf[:], uint64(len(data)))) - b.size += uint64(len(data)) - } - - // Add the event. - b.events = append(b.events, ev) -} - -// writeEventsTo emits events in the batch, including the batch header, to tw. -func (b *Batch) writeEventsTo(tw *raw.TextWriter) { - tw.WriteEvent(raw.Event{ - Version: version.Go122, - Ev: go122.EvEventBatch, - Args: []uint64{b.gen.gen, uint64(b.thread), uint64(b.timestamp), b.size}, - }) - for _, e := range b.events { - tw.WriteEvent(e) - } -} - -// Seq represents a sequence counter. -type Seq uint64 - -// Time represents a low-level trace timestamp (which does not necessarily -// correspond to nanoseconds, like trace.Time does). -type Time uint64 diff --git a/src/internal/trace/v2/mkexp.bash b/src/internal/trace/v2/mkexp.bash deleted file mode 100755 index 8a737198c8..0000000000 --- a/src/internal/trace/v2/mkexp.bash +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# This script copies this directory to golang.org/x/exp/trace. -# Just point it at an golang.org/x/exp checkout. - -set -e -if [ ! -f mkexp.bash ]; then - echo 'mkexp.bash must be run from $GOROOT/src/internal/trace/v2' 1>&2 - exit 1 -fi - -if [ "$#" -ne 1 ]; then - echo 'mkexp.bash expects one argument: a path to a golang.org/x/exp git checkout' - exit 1 -fi - -# Copy. -mkdir -p $1/trace -cp -r ./* $1/trace - -# Cleanup. - -# Delete mkexp.bash. -rm $1/trace/mkexp.bash - -# Move tools to cmd. Can't be cmd here because dist will try to build them. -mv $1/trace/tools $1/trace/cmd - -# Make some packages internal. -mv $1/trace/raw $1/trace/internal/raw -mv $1/trace/event $1/trace/internal/event -mv $1/trace/version $1/trace/internal/version -mv $1/trace/testtrace $1/trace/internal/testtrace - -# Move the debug commands out of testdata. -mv $1/trace/testdata/cmd $1/trace/cmd - -# Fix up import paths. -find $1/trace -name '*.go' | xargs -- sed -i 's/internal\/trace\/v2/golang.org\/x\/exp\/trace/' -find $1/trace -name '*.go' | xargs -- sed -i 's/golang.org\/x\/exp\/trace\/raw/golang.org\/x\/exp\/trace\/internal\/raw/' -find $1/trace -name '*.go' | xargs -- sed -i 's/golang.org\/x\/exp\/trace\/event/golang.org\/x\/exp\/trace\/internal\/event/' -find $1/trace -name '*.go' | xargs -- sed -i 's/golang.org\/x\/exp\/trace\/event\/go122/golang.org\/x\/exp\/trace\/internal\/event\/go122/' -find $1/trace -name '*.go' | xargs -- sed -i 's/golang.org\/x\/exp\/trace\/version/golang.org\/x\/exp\/trace\/internal\/version/' -find $1/trace -name '*.go' | xargs -- sed -i 's/golang.org\/x\/exp\/trace\/testtrace/golang.org\/x\/exp\/trace\/internal\/testtrace/' - -# Format the files. -find $1/trace -name '*.go' | xargs -- gofmt -w -s diff --git a/src/internal/trace/v2/oldtrace.go b/src/internal/trace/v2/oldtrace.go deleted file mode 100644 index 29b7173a3a..0000000000 --- a/src/internal/trace/v2/oldtrace.go +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright 2024 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. - -// This file implements conversion from old (Go 1.11–Go 1.21) traces to the Go -// 1.22 format. -// -// Most events have direct equivalents in 1.22, at worst requiring arguments to -// be reordered. Some events, such as GoWaiting need to look ahead for follow-up -// events to determine the correct translation. GoSyscall, which is an -// instantaneous event, gets turned into a 1 ns long pair of -// GoSyscallStart+GoSyscallEnd, unless we observe a GoSysBlock, in which case we -// emit a GoSyscallStart+GoSyscallEndBlocked pair with the correct duration -// (i.e. starting at the original GoSyscall). -// -// The resulting trace treats the old trace as a single, large generation, -// sharing a single evTable for all events. -// -// We use a new (compared to what was used for 'go tool trace' in earlier -// versions of Go) parser for old traces that is optimized for speed, low memory -// usage, and minimal GC pressure. It allocates events in batches so that even -// though we have to load the entire trace into memory, the conversion process -// shouldn't result in a doubling of memory usage, even if all converted events -// are kept alive, as we free batches once we're done with them. -// -// The conversion process is lossless. - -package trace - -import ( - "errors" - "fmt" - "internal/trace/v2/event" - "internal/trace/v2/event/go122" - "internal/trace/v2/internal/oldtrace" - "io" -) - -type oldTraceConverter struct { - trace oldtrace.Trace - evt *evTable - preInit bool - createdPreInit map[GoID]struct{} - events oldtrace.Events - extra []Event - extraArr [3]Event - tasks map[TaskID]taskState - seenProcs map[ProcID]struct{} - lastTs Time - procMs map[ProcID]ThreadID - lastStwReason uint64 - - inlineToStringID []uint64 - builtinToStringID []uint64 -} - -const ( - // Block reasons - sForever = iota - sPreempted - sGosched - sSleep - sChanSend - sChanRecv - sNetwork - sSync - sSyncCond - sSelect - sEmpty - sMarkAssistWait - - // STW kinds - sSTWUnknown - sSTWGCMarkTermination - sSTWGCSweepTermination - sSTWWriteHeapDump - sSTWGoroutineProfile - sSTWGoroutineProfileCleanup - sSTWAllGoroutinesStackTrace - sSTWReadMemStats - sSTWAllThreadsSyscall - sSTWGOMAXPROCS - sSTWStartTrace - sSTWStopTrace - sSTWCountPagesInUse - sSTWReadMetricsSlow - sSTWReadMemStatsSlow - sSTWPageCachePagesLeaked - sSTWResetDebugLog - - sLast -) - -func (it *oldTraceConverter) init(pr oldtrace.Trace) error { - it.trace = pr - it.preInit = true - it.createdPreInit = make(map[GoID]struct{}) - it.evt = &evTable{pcs: make(map[uint64]frame)} - it.events = pr.Events - it.extra = it.extraArr[:0] - it.tasks = make(map[TaskID]taskState) - it.seenProcs = make(map[ProcID]struct{}) - it.procMs = make(map[ProcID]ThreadID) - it.lastTs = -1 - - evt := it.evt - - // Convert from oldtracer's Strings map to our dataTable. - var max uint64 - for id, s := range pr.Strings { - evt.strings.insert(stringID(id), s) - if id > max { - max = id - } - } - pr.Strings = nil - - // Add all strings used for UserLog. In the old trace format, these were - // stored inline and didn't have IDs. We generate IDs for them. - if max+uint64(len(pr.InlineStrings)) < max { - return errors.New("trace contains too many strings") - } - var addErr error - add := func(id stringID, s string) { - if err := evt.strings.insert(id, s); err != nil && addErr == nil { - addErr = err - } - } - for id, s := range pr.InlineStrings { - nid := max + 1 + uint64(id) - it.inlineToStringID = append(it.inlineToStringID, nid) - add(stringID(nid), s) - } - max += uint64(len(pr.InlineStrings)) - pr.InlineStrings = nil - - // Add strings that the converter emits explicitly. - if max+uint64(sLast) < max { - return errors.New("trace contains too many strings") - } - it.builtinToStringID = make([]uint64, sLast) - addBuiltin := func(c int, s string) { - nid := max + 1 + uint64(c) - it.builtinToStringID[c] = nid - add(stringID(nid), s) - } - addBuiltin(sForever, "forever") - addBuiltin(sPreempted, "preempted") - addBuiltin(sGosched, "runtime.Gosched") - addBuiltin(sSleep, "sleep") - addBuiltin(sChanSend, "chan send") - addBuiltin(sChanRecv, "chan receive") - addBuiltin(sNetwork, "network") - addBuiltin(sSync, "sync") - addBuiltin(sSyncCond, "sync.(*Cond).Wait") - addBuiltin(sSelect, "select") - addBuiltin(sEmpty, "") - addBuiltin(sMarkAssistWait, "GC mark assist wait for work") - addBuiltin(sSTWUnknown, "") - addBuiltin(sSTWGCMarkTermination, "GC mark termination") - addBuiltin(sSTWGCSweepTermination, "GC sweep termination") - addBuiltin(sSTWWriteHeapDump, "write heap dump") - addBuiltin(sSTWGoroutineProfile, "goroutine profile") - addBuiltin(sSTWGoroutineProfileCleanup, "goroutine profile cleanup") - addBuiltin(sSTWAllGoroutinesStackTrace, "all goroutine stack trace") - addBuiltin(sSTWReadMemStats, "read mem stats") - addBuiltin(sSTWAllThreadsSyscall, "AllThreadsSyscall") - addBuiltin(sSTWGOMAXPROCS, "GOMAXPROCS") - addBuiltin(sSTWStartTrace, "start trace") - addBuiltin(sSTWStopTrace, "stop trace") - addBuiltin(sSTWCountPagesInUse, "CountPagesInUse (test)") - addBuiltin(sSTWReadMetricsSlow, "ReadMetricsSlow (test)") - addBuiltin(sSTWReadMemStatsSlow, "ReadMemStatsSlow (test)") - addBuiltin(sSTWPageCachePagesLeaked, "PageCachePagesLeaked (test)") - addBuiltin(sSTWResetDebugLog, "ResetDebugLog (test)") - - if addErr != nil { - // This should be impossible but let's be safe. - return fmt.Errorf("couldn't add strings: %w", addErr) - } - - it.evt.strings.compactify() - - // Convert stacks. - for id, stk := range pr.Stacks { - evt.stacks.insert(stackID(id), stack{pcs: stk}) - } - - // OPT(dh): if we could share the frame type between this package and - // oldtrace we wouldn't have to copy the map. - for pc, f := range pr.PCs { - evt.pcs[pc] = frame{ - pc: pc, - funcID: stringID(f.Fn), - fileID: stringID(f.File), - line: uint64(f.Line), - } - } - pr.Stacks = nil - pr.PCs = nil - evt.stacks.compactify() - return nil -} - -// next returns the next event, io.EOF if there are no more events, or a -// descriptive error for invalid events. -func (it *oldTraceConverter) next() (Event, error) { - if len(it.extra) > 0 { - ev := it.extra[0] - it.extra = it.extra[1:] - - if len(it.extra) == 0 { - it.extra = it.extraArr[:0] - } - // Two events aren't allowed to fall on the same timestamp in the new API, - // but this may happen when we produce EvGoStatus events - if ev.base.time <= it.lastTs { - ev.base.time = it.lastTs + 1 - } - it.lastTs = ev.base.time - return ev, nil - } - - oev, ok := it.events.Pop() - if !ok { - return Event{}, io.EOF - } - - ev, err := it.convertEvent(oev) - - if err == errSkip { - return it.next() - } else if err != nil { - return Event{}, err - } - - // Two events aren't allowed to fall on the same timestamp in the new API, - // but this may happen when we produce EvGoStatus events - if ev.base.time <= it.lastTs { - ev.base.time = it.lastTs + 1 - } - it.lastTs = ev.base.time - return ev, nil -} - -var errSkip = errors.New("skip event") - -// convertEvent converts an event from the old trace format to zero or more -// events in the new format. Most events translate 1 to 1. Some events don't -// result in an event right away, in which case convertEvent returns errSkip. -// Some events result in more than one new event; in this case, convertEvent -// returns the first event and stores additional events in it.extra. When -// encountering events that oldtrace shouldn't be able to emit, ocnvertEvent -// returns a descriptive error. -func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR error) { - var mappedType event.Type - var mappedArgs timedEventArgs - copy(mappedArgs[:], ev.Args[:]) - - switch ev.Type { - case oldtrace.EvGomaxprocs: - mappedType = go122.EvProcsChange - if it.preInit { - // The first EvGomaxprocs signals the end of trace initialization. At this point we've seen - // all goroutines that already existed at trace begin. - it.preInit = false - for gid := range it.createdPreInit { - // These are goroutines that already existed when tracing started but for which we - // received neither GoWaiting, GoInSyscall, or GoStart. These are goroutines that are in - // the states _Gidle or _Grunnable. - it.extra = append(it.extra, Event{ - ctx: schedCtx{ - // G: GoID(gid), - G: NoGoroutine, - P: NoProc, - M: NoThread, - }, - table: it.evt, - base: baseEvent{ - typ: go122.EvGoStatus, - time: Time(ev.Ts), - args: timedEventArgs{uint64(gid), ^uint64(0), uint64(go122.GoRunnable)}, - }, - }) - } - it.createdPreInit = nil - return Event{}, errSkip - } - case oldtrace.EvProcStart: - it.procMs[ProcID(ev.P)] = ThreadID(ev.Args[0]) - if _, ok := it.seenProcs[ProcID(ev.P)]; ok { - mappedType = go122.EvProcStart - mappedArgs = timedEventArgs{uint64(ev.P)} - } else { - it.seenProcs[ProcID(ev.P)] = struct{}{} - mappedType = go122.EvProcStatus - mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcRunning)} - } - case oldtrace.EvProcStop: - if _, ok := it.seenProcs[ProcID(ev.P)]; ok { - mappedType = go122.EvProcStop - mappedArgs = timedEventArgs{uint64(ev.P)} - } else { - it.seenProcs[ProcID(ev.P)] = struct{}{} - mappedType = go122.EvProcStatus - mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcIdle)} - } - case oldtrace.EvGCStart: - mappedType = go122.EvGCBegin - case oldtrace.EvGCDone: - mappedType = go122.EvGCEnd - case oldtrace.EvSTWStart: - sid := it.builtinToStringID[sSTWUnknown+it.trace.STWReason(ev.Args[0])] - it.lastStwReason = sid - mappedType = go122.EvSTWBegin - mappedArgs = timedEventArgs{uint64(sid)} - case oldtrace.EvSTWDone: - mappedType = go122.EvSTWEnd - mappedArgs = timedEventArgs{it.lastStwReason} - case oldtrace.EvGCSweepStart: - mappedType = go122.EvGCSweepBegin - case oldtrace.EvGCSweepDone: - mappedType = go122.EvGCSweepEnd - case oldtrace.EvGoCreate: - if it.preInit { - it.createdPreInit[GoID(ev.Args[0])] = struct{}{} - return Event{}, errSkip - } - mappedType = go122.EvGoCreate - case oldtrace.EvGoStart: - if it.preInit { - mappedType = go122.EvGoStatus - mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoRunning)} - delete(it.createdPreInit, GoID(ev.Args[0])) - } else { - mappedType = go122.EvGoStart - } - case oldtrace.EvGoStartLabel: - it.extra = []Event{{ - ctx: schedCtx{ - G: GoID(ev.G), - P: ProcID(ev.P), - M: it.procMs[ProcID(ev.P)], - }, - table: it.evt, - base: baseEvent{ - typ: go122.EvGoLabel, - time: Time(ev.Ts), - args: timedEventArgs{ev.Args[2]}, - }, - }} - return Event{ - ctx: schedCtx{ - G: GoID(ev.G), - P: ProcID(ev.P), - M: it.procMs[ProcID(ev.P)], - }, - table: it.evt, - base: baseEvent{ - typ: go122.EvGoStart, - time: Time(ev.Ts), - args: mappedArgs, - }, - }, nil - case oldtrace.EvGoEnd: - mappedType = go122.EvGoDestroy - case oldtrace.EvGoStop: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sForever]), uint64(ev.StkID)} - case oldtrace.EvGoSched: - mappedType = go122.EvGoStop - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sGosched]), uint64(ev.StkID)} - case oldtrace.EvGoPreempt: - mappedType = go122.EvGoStop - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sPreempted]), uint64(ev.StkID)} - case oldtrace.EvGoSleep: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSleep]), uint64(ev.StkID)} - case oldtrace.EvGoBlock: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sEmpty]), uint64(ev.StkID)} - case oldtrace.EvGoUnblock: - mappedType = go122.EvGoUnblock - case oldtrace.EvGoBlockSend: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanSend]), uint64(ev.StkID)} - case oldtrace.EvGoBlockRecv: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanRecv]), uint64(ev.StkID)} - case oldtrace.EvGoBlockSelect: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSelect]), uint64(ev.StkID)} - case oldtrace.EvGoBlockSync: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSync]), uint64(ev.StkID)} - case oldtrace.EvGoBlockCond: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSyncCond]), uint64(ev.StkID)} - case oldtrace.EvGoBlockNet: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sNetwork]), uint64(ev.StkID)} - case oldtrace.EvGoBlockGC: - mappedType = go122.EvGoBlock - mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sMarkAssistWait]), uint64(ev.StkID)} - case oldtrace.EvGoSysCall: - // Look for the next event for the same G to determine if the syscall - // blocked. - blocked := false - it.events.All()(func(nev *oldtrace.Event) bool { - if nev.G != ev.G { - return true - } - // After an EvGoSysCall, the next event on the same G will either be - // EvGoSysBlock to denote a blocking syscall, or some other event - // (or the end of the trace) if the syscall didn't block. - if nev.Type == oldtrace.EvGoSysBlock { - blocked = true - } - return false - }) - if blocked { - mappedType = go122.EvGoSyscallBegin - mappedArgs = timedEventArgs{1: uint64(ev.StkID)} - } else { - // Convert the old instantaneous syscall event to a pair of syscall - // begin and syscall end and give it the shortest possible duration, - // 1ns. - out1 := Event{ - ctx: schedCtx{ - G: GoID(ev.G), - P: ProcID(ev.P), - M: it.procMs[ProcID(ev.P)], - }, - table: it.evt, - base: baseEvent{ - typ: go122.EvGoSyscallBegin, - time: Time(ev.Ts), - args: timedEventArgs{1: uint64(ev.StkID)}, - }, - } - - out2 := Event{ - ctx: out1.ctx, - table: it.evt, - base: baseEvent{ - typ: go122.EvGoSyscallEnd, - time: Time(ev.Ts + 1), - args: timedEventArgs{}, - }, - } - - it.extra = append(it.extra, out2) - return out1, nil - } - - case oldtrace.EvGoSysExit: - mappedType = go122.EvGoSyscallEndBlocked - case oldtrace.EvGoSysBlock: - return Event{}, errSkip - case oldtrace.EvGoWaiting: - mappedType = go122.EvGoStatus - mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoWaiting)} - delete(it.createdPreInit, GoID(ev.Args[0])) - case oldtrace.EvGoInSyscall: - mappedType = go122.EvGoStatus - // In the new tracer, GoStatus with GoSyscall knows what thread the - // syscall is on. In the old tracer, EvGoInSyscall doesn't contain that - // information and all we can do here is specify NoThread. - mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoSyscall)} - delete(it.createdPreInit, GoID(ev.Args[0])) - case oldtrace.EvHeapAlloc: - mappedType = go122.EvHeapAlloc - case oldtrace.EvHeapGoal: - mappedType = go122.EvHeapGoal - case oldtrace.EvGCMarkAssistStart: - mappedType = go122.EvGCMarkAssistBegin - case oldtrace.EvGCMarkAssistDone: - mappedType = go122.EvGCMarkAssistEnd - case oldtrace.EvUserTaskCreate: - mappedType = go122.EvUserTaskBegin - parent := ev.Args[1] - if parent == 0 { - parent = uint64(NoTask) - } - mappedArgs = timedEventArgs{ev.Args[0], parent, ev.Args[2], uint64(ev.StkID)} - name, _ := it.evt.strings.get(stringID(ev.Args[2])) - it.tasks[TaskID(ev.Args[0])] = taskState{name: name, parentID: TaskID(ev.Args[1])} - case oldtrace.EvUserTaskEnd: - mappedType = go122.EvUserTaskEnd - // Event.Task expects the parent and name to be smuggled in extra args - // and as extra strings. - ts, ok := it.tasks[TaskID(ev.Args[0])] - if ok { - delete(it.tasks, TaskID(ev.Args[0])) - mappedArgs = timedEventArgs{ - ev.Args[0], - ev.Args[1], - uint64(ts.parentID), - uint64(it.evt.addExtraString(ts.name)), - } - } else { - mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], uint64(NoTask), uint64(it.evt.addExtraString(""))} - } - case oldtrace.EvUserRegion: - switch ev.Args[1] { - case 0: // start - mappedType = go122.EvUserRegionBegin - case 1: // end - mappedType = go122.EvUserRegionEnd - } - mappedArgs = timedEventArgs{ev.Args[0], ev.Args[2], uint64(ev.StkID)} - case oldtrace.EvUserLog: - mappedType = go122.EvUserLog - mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], it.inlineToStringID[ev.Args[3]], uint64(ev.StkID)} - case oldtrace.EvCPUSample: - mappedType = go122.EvCPUSample - // When emitted by the Go 1.22 tracer, CPU samples have 5 arguments: - // timestamp, M, P, G, stack. However, after they get turned into Event, - // they have the arguments stack, M, P, G. - // - // In Go 1.21, CPU samples did not have Ms. - mappedArgs = timedEventArgs{uint64(ev.StkID), ^uint64(0), uint64(ev.P), ev.G} - default: - return Event{}, fmt.Errorf("unexpected event type %v", ev.Type) - } - - if oldtrace.EventDescriptions[ev.Type].Stack { - if stackIDs := go122.Specs()[mappedType].StackIDs; len(stackIDs) > 0 { - mappedArgs[stackIDs[0]-1] = uint64(ev.StkID) - } - } - - m := NoThread - if ev.P != -1 && ev.Type != oldtrace.EvCPUSample { - if t, ok := it.procMs[ProcID(ev.P)]; ok { - m = ThreadID(t) - } - } - if ev.Type == oldtrace.EvProcStop { - delete(it.procMs, ProcID(ev.P)) - } - g := GoID(ev.G) - if g == 0 { - g = NoGoroutine - } - out := Event{ - ctx: schedCtx{ - G: GoID(g), - P: ProcID(ev.P), - M: m, - }, - table: it.evt, - base: baseEvent{ - typ: mappedType, - time: Time(ev.Ts), - args: mappedArgs, - }, - } - return out, nil -} - -// convertOldFormat takes a fully loaded trace in the old trace format and -// returns an iterator over events in the new format. -func convertOldFormat(pr oldtrace.Trace) *oldTraceConverter { - it := &oldTraceConverter{} - it.init(pr) - return it -} diff --git a/src/internal/trace/v2/oldtrace_test.go b/src/internal/trace/v2/oldtrace_test.go deleted file mode 100644 index e315b48160..0000000000 --- a/src/internal/trace/v2/oldtrace_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2024 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 trace_test - -import ( - "internal/trace/v2" - "internal/trace/v2/testtrace" - "io" - "os" - "path/filepath" - "testing" -) - -func TestOldtrace(t *testing.T) { - traces, err := filepath.Glob("./internal/oldtrace/testdata/*_good") - if err != nil { - t.Fatalf("failed to glob for tests: %s", err) - } - var testedUserRegions bool - for _, p := range traces { - p := p - testName, err := filepath.Rel("./internal/oldtrace/testdata", p) - if err != nil { - t.Fatalf("failed to relativize testdata path: %s", err) - } - t.Run(testName, func(t *testing.T) { - f, err := os.Open(p) - if err != nil { - t.Fatalf("failed to open test %q: %s", p, err) - } - defer f.Close() - - tr, err := trace.NewReader(f) - if err != nil { - t.Fatalf("failed to create reader: %s", err) - } - - v := testtrace.NewValidator() - v.Go121 = true - for { - ev, err := tr.ReadEvent() - if err != nil { - if err == io.EOF { - break - } - t.Fatalf("couldn't read converted event: %s", err) - } - if err := v.Event(ev); err != nil { - t.Fatalf("converted event did not validate; event: \n%s\nerror: %s", ev, err) - } - - if testName == "user_task_region_1_21_good" { - testedUserRegions = true - validRegions := map[string]struct{}{ - "post-existing region": struct{}{}, - "region0": struct{}{}, - "region1": struct{}{}, - } - // Check that we correctly convert user regions. These - // strings were generated by - // runtime/trace.TestUserTaskRegion, which is the basis for - // the user_task_region_* test cases. We only check for the - // Go 1.21 traces because earlier traces used different - // strings. - switch ev.Kind() { - case trace.EventRegionBegin, trace.EventRegionEnd: - if _, ok := validRegions[ev.Region().Type]; !ok { - t.Fatalf("converted event has unexpected region type:\n%s", ev) - } - case trace.EventTaskBegin, trace.EventTaskEnd: - if ev.Task().Type != "task0" { - t.Fatalf("converted event has unexpected task type name:\n%s", ev) - } - case trace.EventLog: - l := ev.Log() - if l.Task != 1 || l.Category != "key0" || l.Message != "0123456789abcdef" { - t.Fatalf("converted event has unexpected user log:\n%s", ev) - } - } - } - } - }) - } - if !testedUserRegions { - t.Fatal("didn't see expected test case user_task_region_1_21_good") - } -} diff --git a/src/internal/trace/v2/order.go b/src/internal/trace/v2/order.go deleted file mode 100644 index 3d201513eb..0000000000 --- a/src/internal/trace/v2/order.go +++ /dev/null @@ -1,1399 +0,0 @@ -// 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. - -package trace - -import ( - "fmt" - "strings" - - "internal/trace/v2/event" - "internal/trace/v2/event/go122" - "internal/trace/v2/version" -) - -// ordering emulates Go scheduler state for both validation and -// for putting events in the right order. -// -// The interface to ordering consists of two methods: Advance -// and Next. Advance is called to try and advance an event and -// add completed events to the ordering. Next is used to pick -// off events in the ordering. -type ordering struct { - gStates map[GoID]*gState - pStates map[ProcID]*pState // TODO: The keys are dense, so this can be a slice. - mStates map[ThreadID]*mState - activeTasks map[TaskID]taskState - gcSeq uint64 - gcState gcState - initialGen uint64 - queue queue[Event] -} - -// Advance checks if it's valid to proceed with ev which came from thread m. -// -// It assumes the gen value passed to it is monotonically increasing across calls. -// -// If any error is returned, then the trace is broken and trace parsing must cease. -// If it's not valid to advance with ev, but no error was encountered, the caller -// should attempt to advance with other candidate events from other threads. If the -// caller runs out of candidates, the trace is invalid. -// -// If this returns true, Next is guaranteed to return a complete event. However, -// multiple events may be added to the ordering, so the caller should (but is not -// required to) continue to call Next until it is exhausted. -func (o *ordering) Advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) (bool, error) { - if o.initialGen == 0 { - // Set the initial gen if necessary. - o.initialGen = gen - } - - var curCtx, newCtx schedCtx - curCtx.M = m - newCtx.M = m - - var ms *mState - if m == NoThread { - curCtx.P = NoProc - curCtx.G = NoGoroutine - newCtx = curCtx - } else { - // Pull out or create the mState for this event. - var ok bool - ms, ok = o.mStates[m] - if !ok { - ms = &mState{ - g: NoGoroutine, - p: NoProc, - } - o.mStates[m] = ms - } - curCtx.P = ms.p - curCtx.G = ms.g - newCtx = curCtx - } - - f := orderingDispatch[ev.typ] - if f == nil { - return false, fmt.Errorf("bad event type found while ordering: %v", ev.typ) - } - newCtx, ok, err := f(o, ev, evt, m, gen, curCtx) - if err == nil && ok && ms != nil { - // Update the mState for this event. - ms.p = newCtx.P - ms.g = newCtx.G - } - return ok, err -} - -type orderingHandleFunc func(o *ordering, ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) - -var orderingDispatch = [256]orderingHandleFunc{ - // Procs. - go122.EvProcsChange: (*ordering).advanceAnnotation, - go122.EvProcStart: (*ordering).advanceProcStart, - go122.EvProcStop: (*ordering).advanceProcStop, - go122.EvProcSteal: (*ordering).advanceProcSteal, - go122.EvProcStatus: (*ordering).advanceProcStatus, - - // Goroutines. - go122.EvGoCreate: (*ordering).advanceGoCreate, - go122.EvGoCreateSyscall: (*ordering).advanceGoCreateSyscall, - go122.EvGoStart: (*ordering).advanceGoStart, - go122.EvGoDestroy: (*ordering).advanceGoStopExec, - go122.EvGoDestroySyscall: (*ordering).advanceGoDestroySyscall, - go122.EvGoStop: (*ordering).advanceGoStopExec, - go122.EvGoBlock: (*ordering).advanceGoStopExec, - go122.EvGoUnblock: (*ordering).advanceGoUnblock, - go122.EvGoSyscallBegin: (*ordering).advanceGoSyscallBegin, - go122.EvGoSyscallEnd: (*ordering).advanceGoSyscallEnd, - go122.EvGoSyscallEndBlocked: (*ordering).advanceGoSyscallEndBlocked, - go122.EvGoStatus: (*ordering).advanceGoStatus, - - // STW. - go122.EvSTWBegin: (*ordering).advanceGoRangeBegin, - go122.EvSTWEnd: (*ordering).advanceGoRangeEnd, - - // GC events. - go122.EvGCActive: (*ordering).advanceGCActive, - go122.EvGCBegin: (*ordering).advanceGCBegin, - go122.EvGCEnd: (*ordering).advanceGCEnd, - go122.EvGCSweepActive: (*ordering).advanceGCSweepActive, - go122.EvGCSweepBegin: (*ordering).advanceGCSweepBegin, - go122.EvGCSweepEnd: (*ordering).advanceGCSweepEnd, - go122.EvGCMarkAssistActive: (*ordering).advanceGoRangeActive, - go122.EvGCMarkAssistBegin: (*ordering).advanceGoRangeBegin, - go122.EvGCMarkAssistEnd: (*ordering).advanceGoRangeEnd, - go122.EvHeapAlloc: (*ordering).advanceHeapMetric, - go122.EvHeapGoal: (*ordering).advanceHeapMetric, - - // Annotations. - go122.EvGoLabel: (*ordering).advanceAnnotation, - go122.EvUserTaskBegin: (*ordering).advanceUserTaskBegin, - go122.EvUserTaskEnd: (*ordering).advanceUserTaskEnd, - go122.EvUserRegionBegin: (*ordering).advanceUserRegionBegin, - go122.EvUserRegionEnd: (*ordering).advanceUserRegionEnd, - go122.EvUserLog: (*ordering).advanceAnnotation, - - // Coroutines. Added in Go 1.23. - go122.EvGoSwitch: (*ordering).advanceGoSwitch, - go122.EvGoSwitchDestroy: (*ordering).advanceGoSwitch, - go122.EvGoCreateBlocked: (*ordering).advanceGoCreate, - - // GoStatus event with a stack. Added in Go 1.23. - go122.EvGoStatusStack: (*ordering).advanceGoStatus, - - // Experimental events. - - // Experimental heap span events. Added in Go 1.23. - go122.EvSpan: (*ordering).advanceAllocFree, - go122.EvSpanAlloc: (*ordering).advanceAllocFree, - go122.EvSpanFree: (*ordering).advanceAllocFree, - - // Experimental heap object events. Added in Go 1.23. - go122.EvHeapObject: (*ordering).advanceAllocFree, - go122.EvHeapObjectAlloc: (*ordering).advanceAllocFree, - go122.EvHeapObjectFree: (*ordering).advanceAllocFree, - - // Experimental goroutine stack events. Added in Go 1.23. - go122.EvGoroutineStack: (*ordering).advanceAllocFree, - go122.EvGoroutineStackAlloc: (*ordering).advanceAllocFree, - go122.EvGoroutineStackFree: (*ordering).advanceAllocFree, -} - -func (o *ordering) advanceProcStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - pid := ProcID(ev.args[0]) - status := go122.ProcStatus(ev.args[1]) - if int(status) >= len(go122ProcStatus2ProcState) { - return curCtx, false, fmt.Errorf("invalid status for proc %d: %d", pid, status) - } - oldState := go122ProcStatus2ProcState[status] - if s, ok := o.pStates[pid]; ok { - if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscall { - // ProcSyscallAbandoned is a special case of ProcSyscall. It indicates a - // potential loss of information, but if we're already in ProcSyscall, - // we haven't lost the relevant information. Promote the status and advance. - oldState = ProcRunning - ev.args[1] = uint64(go122.ProcSyscall) - } else if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscallAbandoned { - // If we're passing through ProcSyscallAbandoned, then there's no promotion - // to do. We've lost the M that this P is associated with. However it got there, - // it's going to appear as idle in the API, so pass through as idle. - oldState = ProcIdle - ev.args[1] = uint64(go122.ProcSyscallAbandoned) - } else if s.status != status { - return curCtx, false, fmt.Errorf("inconsistent status for proc %d: old %v vs. new %v", pid, s.status, status) - } - s.seq = makeSeq(gen, 0) // Reset seq. - } else { - o.pStates[pid] = &pState{id: pid, status: status, seq: makeSeq(gen, 0)} - if gen == o.initialGen { - oldState = ProcUndetermined - } else { - oldState = ProcNotExist - } - } - ev.extra(version.Go122)[0] = uint64(oldState) // Smuggle in the old state for StateTransition. - - // Bind the proc to the new context, if it's running. - newCtx := curCtx - if status == go122.ProcRunning || status == go122.ProcSyscall { - newCtx.P = pid - } - // If we're advancing through ProcSyscallAbandoned *but* oldState is running then we've - // promoted it to ProcSyscall. However, because it's ProcSyscallAbandoned, we know this - // P is about to get stolen and its status very likely isn't being emitted by the same - // thread it was bound to. Since this status is Running -> Running and Running is binding, - // we need to make sure we emit it in the right context: the context to which it is bound. - // Find it, and set our current context to it. - if status == go122.ProcSyscallAbandoned && oldState == ProcRunning { - // N.B. This is slow but it should be fairly rare. - found := false - for mid, ms := range o.mStates { - if ms.p == pid { - curCtx.M = mid - curCtx.P = pid - curCtx.G = ms.g - found = true - } - } - if !found { - return curCtx, false, fmt.Errorf("failed to find sched context for proc %d that's about to be stolen", pid) - } - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceProcStart(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - pid := ProcID(ev.args[0]) - seq := makeSeq(gen, ev.args[1]) - - // Try to advance. We might fail here due to sequencing, because the P hasn't - // had a status emitted, or because we already have a P and we're in a syscall, - // and we haven't observed that it was stolen from us yet. - state, ok := o.pStates[pid] - if !ok || state.status != go122.ProcIdle || !seq.succeeds(state.seq) || curCtx.P != NoProc { - // We can't make an inference as to whether this is bad. We could just be seeing - // a ProcStart on a different M before the proc's state was emitted, or before we - // got to the right point in the trace. - // - // Note that we also don't advance here if we have a P and we're in a syscall. - return curCtx, false, nil - } - // We can advance this P. Check some invariants. - // - // We might have a goroutine if a goroutine is exiting a syscall. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustNotHave, Goroutine: event.MayHave} - if err := validateCtx(curCtx, reqs); err != nil { - return curCtx, false, err - } - state.status = go122.ProcRunning - state.seq = seq - newCtx := curCtx - newCtx.P = pid - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceProcStop(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // We must be able to advance this P. - // - // There are 2 ways a P can stop: ProcStop and ProcSteal. ProcStop is used when the P - // is stopped by the same M that started it, while ProcSteal is used when another M - // steals the P by stopping it from a distance. - // - // Since a P is bound to an M, and we're stopping on the same M we started, it must - // always be possible to advance the current M's P from a ProcStop. This is also why - // ProcStop doesn't need a sequence number. - state, ok := o.pStates[curCtx.P] - if !ok { - return curCtx, false, fmt.Errorf("event %s for proc (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.P) - } - if state.status != go122.ProcRunning && state.status != go122.ProcSyscall { - return curCtx, false, fmt.Errorf("%s event for proc that's not %s or %s", go122.EventString(ev.typ), go122.ProcRunning, go122.ProcSyscall) - } - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave} - if err := validateCtx(curCtx, reqs); err != nil { - return curCtx, false, err - } - state.status = go122.ProcIdle - newCtx := curCtx - newCtx.P = NoProc - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceProcSteal(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - pid := ProcID(ev.args[0]) - seq := makeSeq(gen, ev.args[1]) - state, ok := o.pStates[pid] - if !ok || (state.status != go122.ProcSyscall && state.status != go122.ProcSyscallAbandoned) || !seq.succeeds(state.seq) { - // We can't make an inference as to whether this is bad. We could just be seeing - // a ProcStart on a different M before the proc's state was emitted, or before we - // got to the right point in the trace. - return curCtx, false, nil - } - // We can advance this P. Check some invariants. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave} - if err := validateCtx(curCtx, reqs); err != nil { - return curCtx, false, err - } - // Smuggle in the P state that let us advance so we can surface information to the event. - // Specifically, we need to make sure that the event is interpreted not as a transition of - // ProcRunning -> ProcIdle but ProcIdle -> ProcIdle instead. - // - // ProcRunning is binding, but we may be running with a P on the current M and we can't - // bind another P. This P is about to go ProcIdle anyway. - oldStatus := state.status - ev.extra(version.Go122)[0] = uint64(oldStatus) - - // Update the P's status and sequence number. - state.status = go122.ProcIdle - state.seq = seq - - // If we've lost information then don't try to do anything with the M. - // It may have moved on and we can't be sure. - if oldStatus == go122.ProcSyscallAbandoned { - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil - } - - // Validate that the M we're stealing from is what we expect. - mid := ThreadID(ev.args[2]) // The M we're stealing from. - - newCtx := curCtx - if mid == curCtx.M { - // We're stealing from ourselves. This behaves like a ProcStop. - if curCtx.P != pid { - return curCtx, false, fmt.Errorf("tried to self-steal proc %d (thread %d), but got proc %d instead", pid, mid, curCtx.P) - } - newCtx.P = NoProc - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil - } - - // We're stealing from some other M. - mState, ok := o.mStates[mid] - if !ok { - return curCtx, false, fmt.Errorf("stole proc from non-existent thread %d", mid) - } - - // Make sure we're actually stealing the right P. - if mState.p != pid { - return curCtx, false, fmt.Errorf("tried to steal proc %d from thread %d, but got proc %d instead", pid, mid, mState.p) - } - - // Tell the M it has no P so it can proceed. - // - // This is safe because we know the P was in a syscall and - // the other M must be trying to get out of the syscall. - // GoSyscallEndBlocked cannot advance until the corresponding - // M loses its P. - mState.p = NoProc - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceGoStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - gid := GoID(ev.args[0]) - mid := ThreadID(ev.args[1]) - status := go122.GoStatus(ev.args[2]) - - if int(status) >= len(go122GoStatus2GoState) { - return curCtx, false, fmt.Errorf("invalid status for goroutine %d: %d", gid, status) - } - oldState := go122GoStatus2GoState[status] - if s, ok := o.gStates[gid]; ok { - if s.status != status { - return curCtx, false, fmt.Errorf("inconsistent status for goroutine %d: old %v vs. new %v", gid, s.status, status) - } - s.seq = makeSeq(gen, 0) // Reset seq. - } else if gen == o.initialGen { - // Set the state. - o.gStates[gid] = &gState{id: gid, status: status, seq: makeSeq(gen, 0)} - oldState = GoUndetermined - } else { - return curCtx, false, fmt.Errorf("found goroutine status for new goroutine after the first generation: id=%v status=%v", gid, status) - } - ev.extra(version.Go122)[0] = uint64(oldState) // Smuggle in the old state for StateTransition. - - newCtx := curCtx - switch status { - case go122.GoRunning: - // Bind the goroutine to the new context, since it's running. - newCtx.G = gid - case go122.GoSyscall: - if mid == NoThread { - return curCtx, false, fmt.Errorf("found goroutine %d in syscall without a thread", gid) - } - // Is the syscall on this thread? If so, bind it to the context. - // Otherwise, we're talking about a G sitting in a syscall on an M. - // Validate the named M. - if mid == curCtx.M { - if gen != o.initialGen && curCtx.G != gid { - // If this isn't the first generation, we *must* have seen this - // binding occur already. Even if the G was blocked in a syscall - // for multiple generations since trace start, we would have seen - // a previous GoStatus event that bound the goroutine to an M. - return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, curCtx.G) - } - newCtx.G = gid - break - } - // Now we're talking about a thread and goroutine that have been - // blocked on a syscall for the entire generation. This case must - // not have a P; the runtime makes sure that all Ps are traced at - // the beginning of a generation, which involves taking a P back - // from every thread. - ms, ok := o.mStates[mid] - if ok { - // This M has been seen. That means we must have seen this - // goroutine go into a syscall on this thread at some point. - if ms.g != gid { - // But the G on the M doesn't match. Something's wrong. - return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, ms.g) - } - // This case is just a Syscall->Syscall event, which needs to - // appear as having the G currently bound to this M. - curCtx.G = ms.g - } else if !ok { - // The M hasn't been seen yet. That means this goroutine - // has just been sitting in a syscall on this M. Create - // a state for it. - o.mStates[mid] = &mState{g: gid, p: NoProc} - // Don't set curCtx.G in this case because this event is the - // binding event (and curCtx represents the "before" state). - } - // Update the current context to the M we're talking about. - curCtx.M = mid - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceGoCreate(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Goroutines must be created on a running P, but may or may not be created - // by a running goroutine. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave} - if err := validateCtx(curCtx, reqs); err != nil { - return curCtx, false, err - } - // If we have a goroutine, it must be running. - if state, ok := o.gStates[curCtx.G]; ok && state.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) - } - // This goroutine created another. Add a state for it. - newgid := GoID(ev.args[0]) - if _, ok := o.gStates[newgid]; ok { - return curCtx, false, fmt.Errorf("tried to create goroutine (%v) that already exists", newgid) - } - status := go122.GoRunnable - if ev.typ == go122.EvGoCreateBlocked { - status = go122.GoWaiting - } - o.gStates[newgid] = &gState{id: newgid, status: status, seq: makeSeq(gen, 0)} - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGoStopExec(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // These are goroutine events that all require an active running - // goroutine on some thread. They must *always* be advance-able, - // since running goroutines are bound to their M. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - state, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) - } - if state.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) - } - // Handle each case slightly differently; we just group them together - // because they have shared preconditions. - newCtx := curCtx - switch ev.typ { - case go122.EvGoDestroy: - // This goroutine is exiting itself. - delete(o.gStates, curCtx.G) - newCtx.G = NoGoroutine - case go122.EvGoStop: - // Goroutine stopped (yielded). It's runnable but not running on this M. - state.status = go122.GoRunnable - newCtx.G = NoGoroutine - case go122.EvGoBlock: - // Goroutine blocked. It's waiting now and not running on this M. - state.status = go122.GoWaiting - newCtx.G = NoGoroutine - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceGoStart(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - gid := GoID(ev.args[0]) - seq := makeSeq(gen, ev.args[1]) - state, ok := o.gStates[gid] - if !ok || state.status != go122.GoRunnable || !seq.succeeds(state.seq) { - // We can't make an inference as to whether this is bad. We could just be seeing - // a GoStart on a different M before the goroutine was created, before it had its - // state emitted, or before we got to the right point in the trace yet. - return curCtx, false, nil - } - // We can advance this goroutine. Check some invariants. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MustNotHave} - if err := validateCtx(curCtx, reqs); err != nil { - return curCtx, false, err - } - state.status = go122.GoRunning - state.seq = seq - newCtx := curCtx - newCtx.G = gid - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceGoUnblock(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // N.B. These both reference the goroutine to unblock, not the current goroutine. - gid := GoID(ev.args[0]) - seq := makeSeq(gen, ev.args[1]) - state, ok := o.gStates[gid] - if !ok || state.status != go122.GoWaiting || !seq.succeeds(state.seq) { - // We can't make an inference as to whether this is bad. We could just be seeing - // a GoUnblock on a different M before the goroutine was created and blocked itself, - // before it had its state emitted, or before we got to the right point in the trace yet. - return curCtx, false, nil - } - state.status = go122.GoRunnable - state.seq = seq - // N.B. No context to validate. Basically anything can unblock - // a goroutine (e.g. sysmon). - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGoSwitch(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // GoSwitch and GoSwitchDestroy represent a trio of events: - // - Unblock of the goroutine to switch to. - // - Block or destroy of the current goroutine. - // - Start executing the next goroutine. - // - // Because it acts like a GoStart for the next goroutine, we can - // only advance it if the sequence numbers line up. - // - // The current goroutine on the thread must be actively running. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - curGState, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) - } - if curGState.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) - } - nextg := GoID(ev.args[0]) - seq := makeSeq(gen, ev.args[1]) // seq is for nextg, not curCtx.G. - nextGState, ok := o.gStates[nextg] - if !ok || nextGState.status != go122.GoWaiting || !seq.succeeds(nextGState.seq) { - // We can't make an inference as to whether this is bad. We could just be seeing - // a GoSwitch on a different M before the goroutine was created, before it had its - // state emitted, or before we got to the right point in the trace yet. - return curCtx, false, nil - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - - // Update the state of the executing goroutine and emit an event for it - // (GoSwitch and GoSwitchDestroy will be interpreted as GoUnblock events - // for nextg). - switch ev.typ { - case go122.EvGoSwitch: - // Goroutine blocked. It's waiting now and not running on this M. - curGState.status = go122.GoWaiting - - // Emit a GoBlock event. - // TODO(mknyszek): Emit a reason. - o.queue.push(makeEvent(evt, curCtx, go122.EvGoBlock, ev.time, 0 /* no reason */, 0 /* no stack */)) - case go122.EvGoSwitchDestroy: - // This goroutine is exiting itself. - delete(o.gStates, curCtx.G) - - // Emit a GoDestroy event. - o.queue.push(makeEvent(evt, curCtx, go122.EvGoDestroy, ev.time)) - } - // Update the state of the next goroutine. - nextGState.status = go122.GoRunning - nextGState.seq = seq - newCtx := curCtx - newCtx.G = nextg - - // Queue an event for the next goroutine starting to run. - startCtx := curCtx - startCtx.G = NoGoroutine - o.queue.push(makeEvent(evt, startCtx, go122.EvGoStart, ev.time, uint64(nextg), ev.args[1])) - return newCtx, true, nil -} - -func (o *ordering) advanceGoSyscallBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Entering a syscall requires an active running goroutine with a - // proc on some thread. It is always advancable. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - state, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) - } - if state.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) - } - // Goroutine entered a syscall. It's still running on this P and M. - state.status = go122.GoSyscall - pState, ok := o.pStates[curCtx.P] - if !ok { - return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) - } - pState.status = go122.ProcSyscall - // Validate the P sequence number on the event and advance it. - // - // We have a P sequence number for what is supposed to be a goroutine event - // so that we can correctly model P stealing. Without this sequence number here, - // the syscall from which a ProcSteal event is stealing can be ambiguous in the - // face of broken timestamps. See the go122-syscall-steal-proc-ambiguous test for - // more details. - // - // Note that because this sequence number only exists as a tool for disambiguation, - // we can enforce that we have the right sequence number at this point; we don't need - // to back off and see if any other events will advance. This is a running P. - pSeq := makeSeq(gen, ev.args[0]) - if !pSeq.succeeds(pState.seq) { - return curCtx, false, fmt.Errorf("failed to advance %s: can't make sequence: %s -> %s", go122.EventString(ev.typ), pState.seq, pSeq) - } - pState.seq = pSeq - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGoSyscallEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // This event is always advance-able because it happens on the same - // thread that EvGoSyscallStart happened, and the goroutine can't leave - // that thread until its done. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - state, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) - } - if state.status != go122.GoSyscall { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) - } - state.status = go122.GoRunning - - // Transfer the P back to running from syscall. - pState, ok := o.pStates[curCtx.P] - if !ok { - return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) - } - if pState.status != go122.ProcSyscall { - return curCtx, false, fmt.Errorf("expected proc %d in state %v, but got %v instead", curCtx.P, go122.ProcSyscall, pState.status) - } - pState.status = go122.ProcRunning - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGoSyscallEndBlocked(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // This event becomes advanceable when its P is not in a syscall state - // (lack of a P altogether is also acceptable for advancing). - // The transfer out of ProcSyscall can happen either voluntarily via - // ProcStop or involuntarily via ProcSteal. We may also acquire a new P - // before we get here (after the transfer out) but that's OK: that new - // P won't be in the ProcSyscall state anymore. - // - // Basically: while we have a preemptible P, don't advance, because we - // *know* from the event that we're going to lose it at some point during - // the syscall. We shouldn't advance until that happens. - if curCtx.P != NoProc { - pState, ok := o.pStates[curCtx.P] - if !ok { - return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) - } - if pState.status == go122.ProcSyscall { - return curCtx, false, nil - } - } - // As mentioned above, we may have a P here if we ProcStart - // before this event. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil { - return curCtx, false, err - } - state, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) - } - if state.status != go122.GoSyscall { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) - } - newCtx := curCtx - newCtx.G = NoGoroutine - state.status = go122.GoRunnable - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceGoCreateSyscall(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // This event indicates that a goroutine is effectively - // being created out of a cgo callback. Such a goroutine - // is 'created' in the syscall state. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustNotHave}); err != nil { - return curCtx, false, err - } - // This goroutine is effectively being created. Add a state for it. - newgid := GoID(ev.args[0]) - if _, ok := o.gStates[newgid]; ok { - return curCtx, false, fmt.Errorf("tried to create goroutine (%v) in syscall that already exists", newgid) - } - o.gStates[newgid] = &gState{id: newgid, status: go122.GoSyscall, seq: makeSeq(gen, 0)} - // Goroutine is executing. Bind it to the context. - newCtx := curCtx - newCtx.G = newgid - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceGoDestroySyscall(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // This event indicates that a goroutine created for a - // cgo callback is disappearing, either because the callback - // ending or the C thread that called it is being destroyed. - // - // Also, treat this as if we lost our P too. - // The thread ID may be reused by the platform and we'll get - // really confused if we try to steal the P is this is running - // with later. The new M with the same ID could even try to - // steal back this P from itself! - // - // The runtime is careful to make sure that any GoCreateSyscall - // event will enter the runtime emitting events for reacquiring a P. - // - // Note: we might have a P here. The P might not be released - // eagerly by the runtime, and it might get stolen back later - // (or never again, if the program is going to exit). - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil { - return curCtx, false, err - } - // Check to make sure the goroutine exists in the right state. - state, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) - } - if state.status != go122.GoSyscall { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %v", go122.EventString(ev.typ), GoSyscall) - } - // This goroutine is exiting itself. - delete(o.gStates, curCtx.G) - newCtx := curCtx - newCtx.G = NoGoroutine - - // If we have a proc, then we're dissociating from it now. See the comment at the top of the case. - if curCtx.P != NoProc { - pState, ok := o.pStates[curCtx.P] - if !ok { - return curCtx, false, fmt.Errorf("found invalid proc %d during %s", curCtx.P, go122.EventString(ev.typ)) - } - if pState.status != go122.ProcSyscall { - return curCtx, false, fmt.Errorf("proc %d in unexpected state %s during %s", curCtx.P, pState.status, go122.EventString(ev.typ)) - } - // See the go122-create-syscall-reuse-thread-id test case for more details. - pState.status = go122.ProcSyscallAbandoned - newCtx.P = NoProc - - // Queue an extra self-ProcSteal event. - extra := makeEvent(evt, curCtx, go122.EvProcSteal, ev.time, uint64(curCtx.P)) - extra.base.extra(version.Go122)[0] = uint64(go122.ProcSyscall) - o.queue.push(extra) - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return newCtx, true, nil -} - -func (o *ordering) advanceUserTaskBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Handle tasks. Tasks are interesting because: - // - There's no Begin event required to reference a task. - // - End for a particular task ID can appear multiple times. - // As a result, there's very little to validate. The only - // thing we have to be sure of is that a task didn't begin - // after it had already begun. Task IDs are allowed to be - // reused, so we don't care about a Begin after an End. - id := TaskID(ev.args[0]) - if _, ok := o.activeTasks[id]; ok { - return curCtx, false, fmt.Errorf("task ID conflict: %d", id) - } - // Get the parent ID, but don't validate it. There's no guarantee - // we actually have information on whether it's active. - parentID := TaskID(ev.args[1]) - if parentID == BackgroundTask { - // Note: a value of 0 here actually means no parent, *not* the - // background task. Automatic background task attachment only - // applies to regions. - parentID = NoTask - ev.args[1] = uint64(NoTask) - } - - // Validate the name and record it. We'll need to pass it through to - // EvUserTaskEnd. - nameID := stringID(ev.args[2]) - name, ok := evt.strings.get(nameID) - if !ok { - return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ) - } - o.activeTasks[id] = taskState{name: name, parentID: parentID} - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceUserTaskEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - id := TaskID(ev.args[0]) - if ts, ok := o.activeTasks[id]; ok { - // Smuggle the task info. This may happen in a different generation, - // which may not have the name in its string table. Add it to the extra - // strings table so we can look it up later. - ev.extra(version.Go122)[0] = uint64(ts.parentID) - ev.extra(version.Go122)[1] = uint64(evt.addExtraString(ts.name)) - delete(o.activeTasks, id) - } else { - // Explicitly clear the task info. - ev.extra(version.Go122)[0] = uint64(NoTask) - ev.extra(version.Go122)[1] = uint64(evt.addExtraString("")) - } - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceUserRegionBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - tid := TaskID(ev.args[0]) - nameID := stringID(ev.args[1]) - name, ok := evt.strings.get(nameID) - if !ok { - return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ) - } - gState, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("encountered EvUserRegionBegin without known state for current goroutine %d", curCtx.G) - } - if err := gState.beginRegion(userRegion{tid, name}); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceUserRegionEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - tid := TaskID(ev.args[0]) - nameID := stringID(ev.args[1]) - name, ok := evt.strings.get(nameID) - if !ok { - return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ) - } - gState, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("encountered EvUserRegionEnd without known state for current goroutine %d", curCtx.G) - } - if err := gState.endRegion(userRegion{tid, name}); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -// Handle the GC mark phase. -// -// We have sequence numbers for both start and end because they -// can happen on completely different threads. We want an explicit -// partial order edge between start and end here, otherwise we're -// relying entirely on timestamps to make sure we don't advance a -// GCEnd for a _different_ GC cycle if timestamps are wildly broken. -func (o *ordering) advanceGCActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - seq := ev.args[0] - if gen == o.initialGen { - if o.gcState != gcUndetermined { - return curCtx, false, fmt.Errorf("GCActive in the first generation isn't first GC event") - } - o.gcSeq = seq - o.gcState = gcRunning - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil - } - if seq != o.gcSeq+1 { - // This is not the right GC cycle. - return curCtx, false, nil - } - if o.gcState != gcRunning { - return curCtx, false, fmt.Errorf("encountered GCActive while GC was not in progress") - } - o.gcSeq = seq - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGCBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - seq := ev.args[0] - if o.gcState == gcUndetermined { - o.gcSeq = seq - o.gcState = gcRunning - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil - } - if seq != o.gcSeq+1 { - // This is not the right GC cycle. - return curCtx, false, nil - } - if o.gcState == gcRunning { - return curCtx, false, fmt.Errorf("encountered GCBegin while GC was already in progress") - } - o.gcSeq = seq - o.gcState = gcRunning - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGCEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - seq := ev.args[0] - if seq != o.gcSeq+1 { - // This is not the right GC cycle. - return curCtx, false, nil - } - if o.gcState == gcNotRunning { - return curCtx, false, fmt.Errorf("encountered GCEnd when GC was not in progress") - } - if o.gcState == gcUndetermined { - return curCtx, false, fmt.Errorf("encountered GCEnd when GC was in an undetermined state") - } - o.gcSeq = seq - o.gcState = gcNotRunning - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceAnnotation(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Handle simple instantaneous events that require a G. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceHeapMetric(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Handle allocation metrics, which don't require a G. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGCSweepBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Handle sweep, which is bound to a P and doesn't require a G. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { - return curCtx, false, err - } - if err := o.pStates[curCtx.P].beginRange(makeRangeType(ev.typ, 0)); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGCSweepActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - pid := ProcID(ev.args[0]) - // N.B. In practice Ps can't block while they're sweeping, so this can only - // ever reference curCtx.P. However, be lenient about this like we are with - // GCMarkAssistActive; there's no reason the runtime couldn't change to block - // in the middle of a sweep. - pState, ok := o.pStates[pid] - if !ok { - return curCtx, false, fmt.Errorf("encountered GCSweepActive for unknown proc %d", pid) - } - if err := pState.activeRange(makeRangeType(ev.typ, 0), gen == o.initialGen); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGCSweepEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { - return curCtx, false, err - } - _, err := o.pStates[curCtx.P].endRange(ev.typ) - if err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGoRangeBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Handle special goroutine-bound event ranges. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - desc := stringID(0) - if ev.typ == go122.EvSTWBegin { - desc = stringID(ev.args[0]) - } - gState, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", ev.typ, curCtx.G) - } - if err := gState.beginRange(makeRangeType(ev.typ, desc)); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGoRangeActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - gid := GoID(ev.args[0]) - // N.B. Like GoStatus, this can happen at any time, because it can - // reference a non-running goroutine. Don't check anything about the - // current scheduler context. - gState, ok := o.gStates[gid] - if !ok { - return curCtx, false, fmt.Errorf("uninitialized goroutine %d found during %s", gid, go122.EventString(ev.typ)) - } - if err := gState.activeRange(makeRangeType(ev.typ, 0), gen == o.initialGen); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceGoRangeEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { - return curCtx, false, err - } - gState, ok := o.gStates[curCtx.G] - if !ok { - return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", ev.typ, curCtx.G) - } - desc, err := gState.endRange(ev.typ) - if err != nil { - return curCtx, false, err - } - if ev.typ == go122.EvSTWEnd { - // Smuggle the kind into the event. - // Don't use ev.extra here so we have symmetry with STWBegin. - ev.args[0] = uint64(desc) - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -func (o *ordering) advanceAllocFree(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - // Handle simple instantaneous events that may or may not have a P. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave}); err != nil { - return curCtx, false, err - } - o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) - return curCtx, true, nil -} - -// Next returns the next event in the ordering. -func (o *ordering) Next() (Event, bool) { - return o.queue.pop() -} - -// schedCtx represents the scheduling resources associated with an event. -type schedCtx struct { - G GoID - P ProcID - M ThreadID -} - -// validateCtx ensures that ctx conforms to some reqs, returning an error if -// it doesn't. -func validateCtx(ctx schedCtx, reqs event.SchedReqs) error { - // Check thread requirements. - if reqs.Thread == event.MustHave && ctx.M == NoThread { - return fmt.Errorf("expected a thread but didn't have one") - } else if reqs.Thread == event.MustNotHave && ctx.M != NoThread { - return fmt.Errorf("expected no thread but had one") - } - - // Check proc requirements. - if reqs.Proc == event.MustHave && ctx.P == NoProc { - return fmt.Errorf("expected a proc but didn't have one") - } else if reqs.Proc == event.MustNotHave && ctx.P != NoProc { - return fmt.Errorf("expected no proc but had one") - } - - // Check goroutine requirements. - if reqs.Goroutine == event.MustHave && ctx.G == NoGoroutine { - return fmt.Errorf("expected a goroutine but didn't have one") - } else if reqs.Goroutine == event.MustNotHave && ctx.G != NoGoroutine { - return fmt.Errorf("expected no goroutine but had one") - } - return nil -} - -// gcState is a trinary variable for the current state of the GC. -// -// The third state besides "enabled" and "disabled" is "undetermined." -type gcState uint8 - -const ( - gcUndetermined gcState = iota - gcNotRunning - gcRunning -) - -// String returns a human-readable string for the GC state. -func (s gcState) String() string { - switch s { - case gcUndetermined: - return "Undetermined" - case gcNotRunning: - return "NotRunning" - case gcRunning: - return "Running" - } - return "Bad" -} - -// userRegion represents a unique user region when attached to some gState. -type userRegion struct { - // name must be a resolved string because the string ID for the same - // string may change across generations, but we care about checking - // the value itself. - taskID TaskID - name string -} - -// rangeType is a way to classify special ranges of time. -// -// These typically correspond 1:1 with "Begin" events, but -// they may have an optional subtype that describes the range -// in more detail. -type rangeType struct { - typ event.Type // "Begin" event. - desc stringID // Optional subtype. -} - -// makeRangeType constructs a new rangeType. -func makeRangeType(typ event.Type, desc stringID) rangeType { - if styp := go122.Specs()[typ].StartEv; styp != go122.EvNone { - typ = styp - } - return rangeType{typ, desc} -} - -// gState is the state of a goroutine at a point in the trace. -type gState struct { - id GoID - status go122.GoStatus - seq seqCounter - - // regions are the active user regions for this goroutine. - regions []userRegion - - // rangeState is the state of special time ranges bound to this goroutine. - rangeState -} - -// beginRegion starts a user region on the goroutine. -func (s *gState) beginRegion(r userRegion) error { - s.regions = append(s.regions, r) - return nil -} - -// endRegion ends a user region on the goroutine. -func (s *gState) endRegion(r userRegion) error { - if len(s.regions) == 0 { - // We do not know about regions that began before tracing started. - return nil - } - if next := s.regions[len(s.regions)-1]; next != r { - return fmt.Errorf("misuse of region in goroutine %v: region end %v when the inner-most active region start event is %v", s.id, r, next) - } - s.regions = s.regions[:len(s.regions)-1] - return nil -} - -// pState is the state of a proc at a point in the trace. -type pState struct { - id ProcID - status go122.ProcStatus - seq seqCounter - - // rangeState is the state of special time ranges bound to this proc. - rangeState -} - -// mState is the state of a thread at a point in the trace. -type mState struct { - g GoID // Goroutine bound to this M. (The goroutine's state is Executing.) - p ProcID // Proc bound to this M. (The proc's state is Executing.) -} - -// rangeState represents the state of special time ranges. -type rangeState struct { - // inFlight contains the rangeTypes of any ranges bound to a resource. - inFlight []rangeType -} - -// beginRange begins a special range in time on the goroutine. -// -// Returns an error if the range is already in progress. -func (s *rangeState) beginRange(typ rangeType) error { - if s.hasRange(typ) { - return fmt.Errorf("discovered event already in-flight for when starting event %v", go122.Specs()[typ.typ].Name) - } - s.inFlight = append(s.inFlight, typ) - return nil -} - -// activeRange marks special range in time on the goroutine as active in the -// initial generation, or confirms that it is indeed active in later generations. -func (s *rangeState) activeRange(typ rangeType, isInitialGen bool) error { - if isInitialGen { - if s.hasRange(typ) { - return fmt.Errorf("found named active range already in first gen: %v", typ) - } - s.inFlight = append(s.inFlight, typ) - } else if !s.hasRange(typ) { - return fmt.Errorf("resource is missing active range: %v %v", go122.Specs()[typ.typ].Name, s.inFlight) - } - return nil -} - -// hasRange returns true if a special time range on the goroutine as in progress. -func (s *rangeState) hasRange(typ rangeType) bool { - for _, ftyp := range s.inFlight { - if ftyp == typ { - return true - } - } - return false -} - -// endsRange ends a special range in time on the goroutine. -// -// This must line up with the start event type of the range the goroutine is currently in. -func (s *rangeState) endRange(typ event.Type) (stringID, error) { - st := go122.Specs()[typ].StartEv - idx := -1 - for i, r := range s.inFlight { - if r.typ == st { - idx = i - break - } - } - if idx < 0 { - return 0, fmt.Errorf("tried to end event %v, but not in-flight", go122.Specs()[st].Name) - } - // Swap remove. - desc := s.inFlight[idx].desc - s.inFlight[idx], s.inFlight[len(s.inFlight)-1] = s.inFlight[len(s.inFlight)-1], s.inFlight[idx] - s.inFlight = s.inFlight[:len(s.inFlight)-1] - return desc, nil -} - -// seqCounter represents a global sequence counter for a resource. -type seqCounter struct { - gen uint64 // The generation for the local sequence counter seq. - seq uint64 // The sequence number local to the generation. -} - -// makeSeq creates a new seqCounter. -func makeSeq(gen, seq uint64) seqCounter { - return seqCounter{gen: gen, seq: seq} -} - -// succeeds returns true if a is the immediate successor of b. -func (a seqCounter) succeeds(b seqCounter) bool { - return a.gen == b.gen && a.seq == b.seq+1 -} - -// String returns a debug string representation of the seqCounter. -func (c seqCounter) String() string { - return fmt.Sprintf("%d (gen=%d)", c.seq, c.gen) -} - -func dumpOrdering(order *ordering) string { - var sb strings.Builder - for id, state := range order.gStates { - fmt.Fprintf(&sb, "G %d [status=%s seq=%s]\n", id, state.status, state.seq) - } - fmt.Fprintln(&sb) - for id, state := range order.pStates { - fmt.Fprintf(&sb, "P %d [status=%s seq=%s]\n", id, state.status, state.seq) - } - fmt.Fprintln(&sb) - for id, state := range order.mStates { - fmt.Fprintf(&sb, "M %d [g=%d p=%d]\n", id, state.g, state.p) - } - fmt.Fprintln(&sb) - fmt.Fprintf(&sb, "GC %d %s\n", order.gcSeq, order.gcState) - return sb.String() -} - -// taskState represents an active task. -type taskState struct { - // name is the type of the active task. - name string - - // parentID is the parent ID of the active task. - parentID TaskID -} - -// queue implements a growable ring buffer with a queue API. -type queue[T any] struct { - start, end int - buf []T -} - -// push adds a new event to the back of the queue. -func (q *queue[T]) push(value T) { - if q.end-q.start == len(q.buf) { - q.grow() - } - q.buf[q.end%len(q.buf)] = value - q.end++ -} - -// grow increases the size of the queue. -func (q *queue[T]) grow() { - if len(q.buf) == 0 { - q.buf = make([]T, 2) - return - } - - // Create new buf and copy data over. - newBuf := make([]T, len(q.buf)*2) - pivot := q.start % len(q.buf) - first, last := q.buf[pivot:], q.buf[:pivot] - copy(newBuf[:len(first)], first) - copy(newBuf[len(first):], last) - - // Update the queue state. - q.start = 0 - q.end = len(q.buf) - q.buf = newBuf -} - -// pop removes an event from the front of the queue. If the -// queue is empty, it returns an EventBad event. -func (q *queue[T]) pop() (T, bool) { - if q.end-q.start == 0 { - return *new(T), false - } - elem := &q.buf[q.start%len(q.buf)] - value := *elem - *elem = *new(T) // Clear the entry before returning, so we don't hold onto old tables. - q.start++ - return value, true -} - -// makeEvent creates an Event from the provided information. -// -// It's just a convenience function; it's always OK to construct -// an Event manually if this isn't quite the right way to express -// the contents of the event. -func makeEvent(table *evTable, ctx schedCtx, typ event.Type, time Time, args ...uint64) Event { - ev := Event{ - table: table, - ctx: ctx, - base: baseEvent{ - typ: typ, - time: time, - }, - } - copy(ev.base.args[:], args) - return ev -} diff --git a/src/internal/trace/v2/order_test.go b/src/internal/trace/v2/order_test.go deleted file mode 100644 index e267512a53..0000000000 --- a/src/internal/trace/v2/order_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// 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. - -package trace - -import "testing" - -func TestQueue(t *testing.T) { - var q queue[int] - check := func(name string, exp []int) { - for _, v := range exp { - q.push(v) - } - for i, want := range exp { - if got, ok := q.pop(); !ok { - t.Fatalf("check %q: expected to be able to pop after %d pops", name, i+1) - } else if got != want { - t.Fatalf("check %q: expected value %d after on pop %d, got %d", name, want, i+1, got) - } - } - if _, ok := q.pop(); ok { - t.Fatalf("check %q: did not expect to be able to pop more values", name) - } - if _, ok := q.pop(); ok { - t.Fatalf("check %q: did not expect to be able to pop more values a second time", name) - } - } - check("one element", []int{4}) - check("two elements", []int{64, 12}) - check("six elements", []int{55, 16423, 2352, 644, 12874, 9372}) - check("one element again", []int{7}) - check("two elements again", []int{77, 6336}) -} diff --git a/src/internal/trace/v2/raw/doc.go b/src/internal/trace/v2/raw/doc.go deleted file mode 100644 index 53487372b7..0000000000 --- a/src/internal/trace/v2/raw/doc.go +++ /dev/null @@ -1,66 +0,0 @@ -// 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. - -/* -Package raw provides an interface to interpret and emit Go execution traces. -It can interpret and emit execution traces in its wire format as well as a -bespoke but simple text format. - -The readers and writers in this package perform no validation on or ordering of -the input, and so are generally unsuitable for analysis. However, they're very -useful for testing and debugging the tracer in the runtime and more sophisticated -trace parsers. - -# Text format specification - -The trace text format produced and consumed by this package is a line-oriented -format. - -The first line in each text trace is the header line. - - Trace Go1.XX - -Following that is a series of event lines. Each event begins with an -event name, followed by zero or more named unsigned integer arguments. -Names are separated from their integer values by an '=' sign. Names can -consist of any UTF-8 character except '='. - -For example: - - EventName arg1=23 arg2=55 arg3=53 - -Any amount of whitespace is allowed to separate each token. Whitespace -is identified via unicode.IsSpace. - -Some events have additional data on following lines. There are two such -special cases. - -The first special case consists of events with trailing byte-oriented data. -The trailer begins on the following line from the event. That line consists -of a single argument 'data' and a Go-quoted string representing the byte data -within. Note: an explicit argument for the length is elided, because it's -just the length of the unquoted string. - -For example: - - String id=5 - data="hello world\x00" - -These events are identified in their spec by the HasData flag. - -The second special case consists of stack events. These events are identified -by the IsStack flag. These events also have a trailing unsigned integer argument -describing the number of stack frame descriptors that follow. Each stack frame -descriptor is on its own line following the event, consisting of four signed -integer arguments: the PC, an integer describing the function name, an integer -describing the file name, and the line number in that file that function was at -at the time the stack trace was taken. - -For example: - - Stack id=5 n=2 - pc=1241251 func=3 file=6 line=124 - pc=7534345 func=6 file=3 line=64 -*/ -package raw diff --git a/src/internal/trace/v2/raw/event.go b/src/internal/trace/v2/raw/event.go deleted file mode 100644 index 6f09f1f4c2..0000000000 --- a/src/internal/trace/v2/raw/event.go +++ /dev/null @@ -1,60 +0,0 @@ -// 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. - -package raw - -import ( - "strconv" - "strings" - - "internal/trace/v2/event" - "internal/trace/v2/version" -) - -// Event is a simple representation of a trace event. -// -// Note that this typically includes much more than just -// timestamped events, and it also represents parts of the -// trace format's framing. (But not interpreted.) -type Event struct { - Version version.Version - Ev event.Type - Args []uint64 - Data []byte -} - -// String returns the canonical string representation of the event. -// -// This format is the same format that is parsed by the TextReader -// and emitted by the TextWriter. -func (e *Event) String() string { - spec := e.Version.Specs()[e.Ev] - - var s strings.Builder - s.WriteString(spec.Name) - for i := range spec.Args { - s.WriteString(" ") - s.WriteString(spec.Args[i]) - s.WriteString("=") - s.WriteString(strconv.FormatUint(e.Args[i], 10)) - } - if spec.IsStack { - frames := e.Args[len(spec.Args):] - for i := 0; i < len(frames); i++ { - if i%4 == 0 { - s.WriteString("\n\t") - } else { - s.WriteString(" ") - } - s.WriteString(frameFields[i%4]) - s.WriteString("=") - s.WriteString(strconv.FormatUint(frames[i], 10)) - } - } - if e.Data != nil { - s.WriteString("\n\tdata=") - s.WriteString(strconv.Quote(string(e.Data))) - } - return s.String() -} diff --git a/src/internal/trace/v2/raw/reader.go b/src/internal/trace/v2/raw/reader.go deleted file mode 100644 index fdcd47f7c5..0000000000 --- a/src/internal/trace/v2/raw/reader.go +++ /dev/null @@ -1,110 +0,0 @@ -// 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. - -package raw - -import ( - "bufio" - "encoding/binary" - "fmt" - "io" - - "internal/trace/v2/event" - "internal/trace/v2/version" -) - -// Reader parses trace bytes with only very basic validation -// into an event stream. -type Reader struct { - r *bufio.Reader - v version.Version - specs []event.Spec -} - -// NewReader creates a new reader for the trace wire format. -func NewReader(r io.Reader) (*Reader, error) { - br := bufio.NewReader(r) - v, err := version.ReadHeader(br) - if err != nil { - return nil, err - } - return &Reader{r: br, v: v, specs: v.Specs()}, nil -} - -// Version returns the version of the trace that we're reading. -func (r *Reader) Version() version.Version { - return r.v -} - -// ReadEvent reads and returns the next trace event in the byte stream. -func (r *Reader) ReadEvent() (Event, error) { - evb, err := r.r.ReadByte() - if err == io.EOF { - return Event{}, io.EOF - } - if err != nil { - return Event{}, err - } - if int(evb) >= len(r.specs) || evb == 0 { - return Event{}, fmt.Errorf("invalid event type: %d", evb) - } - ev := event.Type(evb) - spec := r.specs[ev] - args, err := r.readArgs(len(spec.Args)) - if err != nil { - return Event{}, err - } - if spec.IsStack { - len := int(args[1]) - for i := 0; i < len; i++ { - // Each stack frame has four args: pc, func ID, file ID, line number. - frame, err := r.readArgs(4) - if err != nil { - return Event{}, err - } - args = append(args, frame...) - } - } - var data []byte - if spec.HasData { - data, err = r.readData() - if err != nil { - return Event{}, err - } - } - return Event{ - Version: r.v, - Ev: ev, - Args: args, - Data: data, - }, nil -} - -func (r *Reader) readArgs(n int) ([]uint64, error) { - var args []uint64 - for i := 0; i < n; i++ { - val, err := binary.ReadUvarint(r.r) - if err != nil { - return nil, err - } - args = append(args, val) - } - return args, nil -} - -func (r *Reader) readData() ([]byte, error) { - len, err := binary.ReadUvarint(r.r) - if err != nil { - return nil, err - } - var data []byte - for i := 0; i < int(len); i++ { - b, err := r.r.ReadByte() - if err != nil { - return nil, err - } - data = append(data, b) - } - return data, nil -} diff --git a/src/internal/trace/v2/raw/textreader.go b/src/internal/trace/v2/raw/textreader.go deleted file mode 100644 index 07785f35a3..0000000000 --- a/src/internal/trace/v2/raw/textreader.go +++ /dev/null @@ -1,217 +0,0 @@ -// 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. - -package raw - -import ( - "bufio" - "fmt" - "io" - "strconv" - "strings" - "unicode" - - "internal/trace/v2/event" - "internal/trace/v2/version" -) - -// TextReader parses a text format trace with only very basic validation -// into an event stream. -type TextReader struct { - v version.Version - specs []event.Spec - names map[string]event.Type - s *bufio.Scanner -} - -// NewTextReader creates a new reader for the trace text format. -func NewTextReader(r io.Reader) (*TextReader, error) { - tr := &TextReader{s: bufio.NewScanner(r)} - line, err := tr.nextLine() - if err != nil { - return nil, err - } - trace, line := readToken(line) - if trace != "Trace" { - return nil, fmt.Errorf("failed to parse header") - } - gover, line := readToken(line) - if !strings.HasPrefix(gover, "Go1.") { - return nil, fmt.Errorf("failed to parse header Go version") - } - rawv, err := strconv.ParseUint(gover[len("Go1."):], 10, 64) - if err != nil { - return nil, fmt.Errorf("failed to parse header Go version: %v", err) - } - v := version.Version(rawv) - if !v.Valid() { - return nil, fmt.Errorf("unknown or unsupported Go version 1.%d", v) - } - tr.v = v - tr.specs = v.Specs() - tr.names = event.Names(tr.specs) - for _, r := range line { - if !unicode.IsSpace(r) { - return nil, fmt.Errorf("encountered unexpected non-space at the end of the header: %q", line) - } - } - return tr, nil -} - -// Version returns the version of the trace that we're reading. -func (r *TextReader) Version() version.Version { - return r.v -} - -// ReadEvent reads and returns the next trace event in the text stream. -func (r *TextReader) ReadEvent() (Event, error) { - line, err := r.nextLine() - if err != nil { - return Event{}, err - } - evStr, line := readToken(line) - ev, ok := r.names[evStr] - if !ok { - return Event{}, fmt.Errorf("unidentified event: %s", evStr) - } - spec := r.specs[ev] - args, err := readArgs(line, spec.Args) - if err != nil { - return Event{}, fmt.Errorf("reading args for %s: %v", evStr, err) - } - if spec.IsStack { - len := int(args[1]) - for i := 0; i < len; i++ { - line, err := r.nextLine() - if err == io.EOF { - return Event{}, fmt.Errorf("unexpected EOF while reading stack: args=%v", args) - } - if err != nil { - return Event{}, err - } - frame, err := readArgs(line, frameFields) - if err != nil { - return Event{}, err - } - args = append(args, frame...) - } - } - var data []byte - if spec.HasData { - line, err := r.nextLine() - if err == io.EOF { - return Event{}, fmt.Errorf("unexpected EOF while reading data for %s: args=%v", evStr, args) - } - if err != nil { - return Event{}, err - } - data, err = readData(line) - if err != nil { - return Event{}, err - } - } - return Event{ - Version: r.v, - Ev: ev, - Args: args, - Data: data, - }, nil -} - -func (r *TextReader) nextLine() (string, error) { - for { - if !r.s.Scan() { - if err := r.s.Err(); err != nil { - return "", err - } - return "", io.EOF - } - txt := r.s.Text() - tok, _ := readToken(txt) - if tok == "" { - continue // Empty line or comment. - } - return txt, nil - } -} - -var frameFields = []string{"pc", "func", "file", "line"} - -func readArgs(s string, names []string) ([]uint64, error) { - var args []uint64 - for _, name := range names { - arg, value, rest, err := readArg(s) - if err != nil { - return nil, err - } - if arg != name { - return nil, fmt.Errorf("expected argument %q, but got %q", name, arg) - } - args = append(args, value) - s = rest - } - for _, r := range s { - if !unicode.IsSpace(r) { - return nil, fmt.Errorf("encountered unexpected non-space at the end of an event: %q", s) - } - } - return args, nil -} - -func readArg(s string) (arg string, value uint64, rest string, err error) { - var tok string - tok, rest = readToken(s) - if len(tok) == 0 { - return "", 0, s, fmt.Errorf("no argument") - } - parts := strings.SplitN(tok, "=", 2) - if len(parts) < 2 { - return "", 0, s, fmt.Errorf("malformed argument: %q", tok) - } - arg = parts[0] - value, err = strconv.ParseUint(parts[1], 10, 64) - if err != nil { - return arg, value, s, fmt.Errorf("failed to parse argument value %q for arg %q", parts[1], parts[0]) - } - return -} - -func readToken(s string) (token, rest string) { - tkStart := -1 - for i, r := range s { - if r == '#' { - return "", "" - } - if !unicode.IsSpace(r) { - tkStart = i - break - } - } - if tkStart < 0 { - return "", "" - } - tkEnd := -1 - for i, r := range s[tkStart:] { - if unicode.IsSpace(r) || r == '#' { - tkEnd = i + tkStart - break - } - } - if tkEnd < 0 { - return s[tkStart:], "" - } - return s[tkStart:tkEnd], s[tkEnd:] -} - -func readData(line string) ([]byte, error) { - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 || strings.TrimSpace(parts[0]) != "data" { - return nil, fmt.Errorf("malformed data: %q", line) - } - data, err := strconv.Unquote(strings.TrimSpace(parts[1])) - if err != nil { - return nil, fmt.Errorf("failed to parse data: %q: %v", line, err) - } - return []byte(data), nil -} diff --git a/src/internal/trace/v2/raw/textwriter.go b/src/internal/trace/v2/raw/textwriter.go deleted file mode 100644 index 367a80bbb6..0000000000 --- a/src/internal/trace/v2/raw/textwriter.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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. - -package raw - -import ( - "fmt" - "io" - - "internal/trace/v2/version" -) - -// TextWriter emits the text format of a trace. -type TextWriter struct { - w io.Writer - v version.Version -} - -// NewTextWriter creates a new write for the trace text format. -func NewTextWriter(w io.Writer, v version.Version) (*TextWriter, error) { - _, err := fmt.Fprintf(w, "Trace Go1.%d\n", v) - if err != nil { - return nil, err - } - return &TextWriter{w: w, v: v}, nil -} - -// WriteEvent writes a single event to the stream. -func (w *TextWriter) WriteEvent(e Event) error { - // Check version. - if e.Version != w.v { - return fmt.Errorf("mismatched version between writer (go 1.%d) and event (go 1.%d)", w.v, e.Version) - } - - // Write event. - _, err := fmt.Fprintln(w.w, e.String()) - return err -} diff --git a/src/internal/trace/v2/raw/writer.go b/src/internal/trace/v2/raw/writer.go deleted file mode 100644 index 80596ebe9a..0000000000 --- a/src/internal/trace/v2/raw/writer.go +++ /dev/null @@ -1,75 +0,0 @@ -// 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. - -package raw - -import ( - "encoding/binary" - "fmt" - "io" - - "internal/trace/v2/event" - "internal/trace/v2/version" -) - -// Writer emits the wire format of a trace. -// -// It may not produce a byte-for-byte compatible trace from what is -// produced by the runtime, because it may be missing extra padding -// in the LEB128 encoding that the runtime adds but isn't necessary -// when you know the data up-front. -type Writer struct { - w io.Writer - buf []byte - v version.Version - specs []event.Spec -} - -// NewWriter creates a new byte format writer. -func NewWriter(w io.Writer, v version.Version) (*Writer, error) { - _, err := version.WriteHeader(w, v) - return &Writer{w: w, v: v, specs: v.Specs()}, err -} - -// WriteEvent writes a single event to the trace wire format stream. -func (w *Writer) WriteEvent(e Event) error { - // Check version. - if e.Version != w.v { - return fmt.Errorf("mismatched version between writer (go 1.%d) and event (go 1.%d)", w.v, e.Version) - } - - // Write event header byte. - w.buf = append(w.buf, uint8(e.Ev)) - - // Write out all arguments. - spec := w.specs[e.Ev] - for _, arg := range e.Args[:len(spec.Args)] { - w.buf = binary.AppendUvarint(w.buf, arg) - } - if spec.IsStack { - frameArgs := e.Args[len(spec.Args):] - for i := 0; i < len(frameArgs); i++ { - w.buf = binary.AppendUvarint(w.buf, frameArgs[i]) - } - } - - // Write out the length of the data. - if spec.HasData { - w.buf = binary.AppendUvarint(w.buf, uint64(len(e.Data))) - } - - // Write out varint events. - _, err := w.w.Write(w.buf) - w.buf = w.buf[:0] - if err != nil { - return err - } - - // Write out data. - if spec.HasData { - _, err := w.w.Write(e.Data) - return err - } - return nil -} diff --git a/src/internal/trace/v2/reader.go b/src/internal/trace/v2/reader.go deleted file mode 100644 index 8f886772bf..0000000000 --- a/src/internal/trace/v2/reader.go +++ /dev/null @@ -1,237 +0,0 @@ -// 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. - -package trace - -import ( - "bufio" - "fmt" - "io" - "slices" - "strings" - - "internal/trace/v2/event/go122" - "internal/trace/v2/internal/oldtrace" - "internal/trace/v2/version" -) - -// Reader reads a byte stream, validates it, and produces trace events. -type Reader struct { - r *bufio.Reader - lastTs Time - gen *generation - spill *spilledBatch - spillErr error // error from reading spill - frontier []*batchCursor - cpuSamples []cpuSample - order ordering - emittedSync bool - - go121Events *oldTraceConverter -} - -// NewReader creates a new trace reader. -func NewReader(r io.Reader) (*Reader, error) { - br := bufio.NewReader(r) - v, err := version.ReadHeader(br) - if err != nil { - return nil, err - } - switch v { - case version.Go111, version.Go119, version.Go121: - tr, err := oldtrace.Parse(br, v) - if err != nil { - return nil, err - } - return &Reader{ - go121Events: convertOldFormat(tr), - }, nil - case version.Go122, version.Go123: - return &Reader{ - r: br, - order: ordering{ - mStates: make(map[ThreadID]*mState), - pStates: make(map[ProcID]*pState), - gStates: make(map[GoID]*gState), - activeTasks: make(map[TaskID]taskState), - }, - // Don't emit a sync event when we first go to emit events. - emittedSync: true, - }, nil - default: - return nil, fmt.Errorf("unknown or unsupported version go 1.%d", v) - } -} - -// ReadEvent reads a single event from the stream. -// -// If the stream has been exhausted, it returns an invalid -// event and io.EOF. -func (r *Reader) ReadEvent() (e Event, err error) { - if r.go121Events != nil { - ev, err := r.go121Events.next() - if err != nil { - // XXX do we have to emit an EventSync when the trace is done? - return Event{}, err - } - return ev, nil - } - - // Go 1.22+ trace parsing algorithm. - // - // (1) Read in all the batches for the next generation from the stream. - // (a) Use the size field in the header to quickly find all batches. - // (2) Parse out the strings, stacks, CPU samples, and timestamp conversion data. - // (3) Group each event batch by M, sorted by timestamp. (batchCursor contains the groups.) - // (4) Organize batchCursors in a min-heap, ordered by the timestamp of the next event for each M. - // (5) Try to advance the next event for the M at the top of the min-heap. - // (a) On success, select that M. - // (b) On failure, sort the min-heap and try to advance other Ms. Select the first M that advances. - // (c) If there's nothing left to advance, goto (1). - // (6) Select the latest event for the selected M and get it ready to be returned. - // (7) Read the next event for the selected M and update the min-heap. - // (8) Return the selected event, goto (5) on the next call. - - // Set us up to track the last timestamp and fix up - // the timestamp of any event that comes through. - defer func() { - if err != nil { - return - } - if err = e.validateTableIDs(); err != nil { - return - } - if e.base.time <= r.lastTs { - e.base.time = r.lastTs + 1 - } - r.lastTs = e.base.time - }() - - // Consume any events in the ordering first. - if ev, ok := r.order.Next(); ok { - return ev, nil - } - - // Check if we need to refresh the generation. - if len(r.frontier) == 0 && len(r.cpuSamples) == 0 { - if !r.emittedSync { - r.emittedSync = true - return syncEvent(r.gen.evTable, r.lastTs), nil - } - if r.spillErr != nil { - return Event{}, r.spillErr - } - if r.gen != nil && r.spill == nil { - // If we have a generation from the last read, - // and there's nothing left in the frontier, and - // there's no spilled batch, indicating that there's - // no further generation, it means we're done. - // Return io.EOF. - return Event{}, io.EOF - } - // Read the next generation. - var err error - r.gen, r.spill, err = readGeneration(r.r, r.spill) - if r.gen == nil { - return Event{}, err - } - r.spillErr = err - - // Reset CPU samples cursor. - r.cpuSamples = r.gen.cpuSamples - - // Reset frontier. - for m, batches := range r.gen.batches { - bc := &batchCursor{m: m} - ok, err := bc.nextEvent(batches, r.gen.freq) - if err != nil { - return Event{}, err - } - if !ok { - // Turns out there aren't actually any events in these batches. - continue - } - r.frontier = heapInsert(r.frontier, bc) - } - - // Reset emittedSync. - r.emittedSync = false - } - tryAdvance := func(i int) (bool, error) { - bc := r.frontier[i] - - if ok, err := r.order.Advance(&bc.ev, r.gen.evTable, bc.m, r.gen.gen); !ok || err != nil { - return ok, err - } - - // Refresh the cursor's event. - ok, err := bc.nextEvent(r.gen.batches[bc.m], r.gen.freq) - if err != nil { - return false, err - } - if ok { - // If we successfully refreshed, update the heap. - heapUpdate(r.frontier, i) - } else { - // There's nothing else to read. Delete this cursor from the frontier. - r.frontier = heapRemove(r.frontier, i) - } - return true, nil - } - // Inject a CPU sample if it comes next. - if len(r.cpuSamples) != 0 { - if len(r.frontier) == 0 || r.cpuSamples[0].time < r.frontier[0].ev.time { - e := r.cpuSamples[0].asEvent(r.gen.evTable) - r.cpuSamples = r.cpuSamples[1:] - return e, nil - } - } - // Try to advance the head of the frontier, which should have the minimum timestamp. - // This should be by far the most common case - if len(r.frontier) == 0 { - return Event{}, fmt.Errorf("broken trace: frontier is empty:\n[gen=%d]\n\n%s\n%s\n", r.gen.gen, dumpFrontier(r.frontier), dumpOrdering(&r.order)) - } - if ok, err := tryAdvance(0); err != nil { - return Event{}, err - } else if !ok { - // Try to advance the rest of the frontier, in timestamp order. - // - // To do this, sort the min-heap. A sorted min-heap is still a - // min-heap, but now we can iterate over the rest and try to - // advance in order. This path should be rare. - slices.SortFunc(r.frontier, (*batchCursor).compare) - success := false - for i := 1; i < len(r.frontier); i++ { - if ok, err = tryAdvance(i); err != nil { - return Event{}, err - } else if ok { - success = true - break - } - } - if !success { - return Event{}, fmt.Errorf("broken trace: failed to advance: frontier:\n[gen=%d]\n\n%s\n%s\n", r.gen.gen, dumpFrontier(r.frontier), dumpOrdering(&r.order)) - } - } - - // Pick off the next event on the queue. At this point, one must exist. - ev, ok := r.order.Next() - if !ok { - panic("invariant violation: advance successful, but queue is empty") - } - return ev, nil -} - -func dumpFrontier(frontier []*batchCursor) string { - var sb strings.Builder - for _, bc := range frontier { - spec := go122.Specs()[bc.ev.typ] - fmt.Fprintf(&sb, "M %d [%s time=%d", bc.m, spec.Name, bc.ev.time) - for i, arg := range spec.Args[1:] { - fmt.Fprintf(&sb, " %s=%d", arg, bc.ev.args[i]) - } - fmt.Fprintf(&sb, "]\n") - } - return sb.String() -} diff --git a/src/internal/trace/v2/reader_test.go b/src/internal/trace/v2/reader_test.go deleted file mode 100644 index 121f0174ed..0000000000 --- a/src/internal/trace/v2/reader_test.go +++ /dev/null @@ -1,172 +0,0 @@ -// 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. - -package trace_test - -import ( - "bytes" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "testing" - - "internal/trace/v2" - "internal/trace/v2/raw" - "internal/trace/v2/testtrace" - "internal/trace/v2/version" -) - -var ( - logEvents = flag.Bool("log-events", false, "whether to log high-level events; significantly slows down tests") - dumpTraces = flag.Bool("dump-traces", false, "dump traces even on success") -) - -func TestReaderGolden(t *testing.T) { - matches, err := filepath.Glob("./testdata/tests/*.test") - if err != nil { - t.Fatalf("failed to glob for tests: %v", err) - } - for _, testPath := range matches { - testPath := testPath - testName, err := filepath.Rel("./testdata", testPath) - if err != nil { - t.Fatalf("failed to relativize testdata path: %v", err) - } - t.Run(testName, func(t *testing.T) { - tr, exp, err := testtrace.ParseFile(testPath) - if err != nil { - t.Fatalf("failed to parse test file at %s: %v", testPath, err) - } - testReader(t, tr, exp) - }) - } -} - -func FuzzReader(f *testing.F) { - // Currently disabled because the parser doesn't do much validation and most - // getters can be made to panic. Turn this on once the parser is meant to - // reject invalid traces. - const testGetters = false - - f.Fuzz(func(t *testing.T, b []byte) { - r, err := trace.NewReader(bytes.NewReader(b)) - if err != nil { - return - } - for { - ev, err := r.ReadEvent() - if err != nil { - break - } - - if !testGetters { - continue - } - // Make sure getters don't do anything that panics - switch ev.Kind() { - case trace.EventLabel: - ev.Label() - case trace.EventLog: - ev.Log() - case trace.EventMetric: - ev.Metric() - case trace.EventRangeActive, trace.EventRangeBegin: - ev.Range() - case trace.EventRangeEnd: - ev.Range() - ev.RangeAttributes() - case trace.EventStateTransition: - ev.StateTransition() - case trace.EventRegionBegin, trace.EventRegionEnd: - ev.Region() - case trace.EventTaskBegin, trace.EventTaskEnd: - ev.Task() - case trace.EventSync: - case trace.EventStackSample: - case trace.EventBad: - } - } - }) -} - -func testReader(t *testing.T, tr io.Reader, exp *testtrace.Expectation) { - r, err := trace.NewReader(tr) - if err != nil { - if err := exp.Check(err); err != nil { - t.Error(err) - } - return - } - v := testtrace.NewValidator() - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - if err := exp.Check(err); err != nil { - t.Error(err) - } - return - } - if *logEvents { - t.Log(ev.String()) - } - if err := v.Event(ev); err != nil { - t.Error(err) - } - } - if err := exp.Check(nil); err != nil { - t.Error(err) - } -} - -func dumpTraceToText(t *testing.T, b []byte) string { - t.Helper() - - br, err := raw.NewReader(bytes.NewReader(b)) - if err != nil { - t.Fatalf("dumping trace: %v", err) - } - var sb strings.Builder - tw, err := raw.NewTextWriter(&sb, version.Current) - if err != nil { - t.Fatalf("dumping trace: %v", err) - } - for { - ev, err := br.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - t.Fatalf("dumping trace: %v", err) - } - if err := tw.WriteEvent(ev); err != nil { - t.Fatalf("dumping trace: %v", err) - } - } - return sb.String() -} - -func dumpTraceToFile(t *testing.T, testName string, stress bool, b []byte) string { - t.Helper() - - desc := "default" - if stress { - desc = "stress" - } - name := fmt.Sprintf("%s.%s.trace.", testName, desc) - f, err := os.CreateTemp("", name) - if err != nil { - t.Fatalf("creating temp file: %v", err) - } - defer f.Close() - if _, err := io.Copy(f, bytes.NewReader(b)); err != nil { - t.Fatalf("writing trace dump to %q: %v", f.Name(), err) - } - return f.Name() -} diff --git a/src/internal/trace/v2/resources.go b/src/internal/trace/v2/resources.go deleted file mode 100644 index f49696f91c..0000000000 --- a/src/internal/trace/v2/resources.go +++ /dev/null @@ -1,274 +0,0 @@ -// 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. - -package trace - -import "fmt" - -// ThreadID is the runtime-internal M structure's ID. This is unique -// for each OS thread. -type ThreadID int64 - -// NoThread indicates that the relevant events don't correspond to any -// thread in particular. -const NoThread = ThreadID(-1) - -// ProcID is the runtime-internal G structure's id field. This is unique -// for each P. -type ProcID int64 - -// NoProc indicates that the relevant events don't correspond to any -// P in particular. -const NoProc = ProcID(-1) - -// GoID is the runtime-internal G structure's goid field. This is unique -// for each goroutine. -type GoID int64 - -// NoGoroutine indicates that the relevant events don't correspond to any -// goroutine in particular. -const NoGoroutine = GoID(-1) - -// GoState represents the state of a goroutine. -// -// New GoStates may be added in the future. Users of this type must be robust -// to that possibility. -type GoState uint8 - -const ( - GoUndetermined GoState = iota // No information is known about the goroutine. - GoNotExist // Goroutine does not exist. - GoRunnable // Goroutine is runnable but not running. - GoRunning // Goroutine is running. - GoWaiting // Goroutine is waiting on something to happen. - GoSyscall // Goroutine is in a system call. -) - -// Executing returns true if the state indicates that the goroutine is executing -// and bound to its thread. -func (s GoState) Executing() bool { - return s == GoRunning || s == GoSyscall -} - -// String returns a human-readable representation of a GoState. -// -// The format of the returned string is for debugging purposes and is subject to change. -func (s GoState) String() string { - switch s { - case GoUndetermined: - return "Undetermined" - case GoNotExist: - return "NotExist" - case GoRunnable: - return "Runnable" - case GoRunning: - return "Running" - case GoWaiting: - return "Waiting" - case GoSyscall: - return "Syscall" - } - return "Bad" -} - -// ProcState represents the state of a proc. -// -// New ProcStates may be added in the future. Users of this type must be robust -// to that possibility. -type ProcState uint8 - -const ( - ProcUndetermined ProcState = iota // No information is known about the proc. - ProcNotExist // Proc does not exist. - ProcRunning // Proc is running. - ProcIdle // Proc is idle. -) - -// Executing returns true if the state indicates that the proc is executing -// and bound to its thread. -func (s ProcState) Executing() bool { - return s == ProcRunning -} - -// String returns a human-readable representation of a ProcState. -// -// The format of the returned string is for debugging purposes and is subject to change. -func (s ProcState) String() string { - switch s { - case ProcUndetermined: - return "Undetermined" - case ProcNotExist: - return "NotExist" - case ProcRunning: - return "Running" - case ProcIdle: - return "Idle" - } - return "Bad" -} - -// ResourceKind indicates a kind of resource that has a state machine. -// -// New ResourceKinds may be added in the future. Users of this type must be robust -// to that possibility. -type ResourceKind uint8 - -const ( - ResourceNone ResourceKind = iota // No resource. - ResourceGoroutine // Goroutine. - ResourceProc // Proc. - ResourceThread // Thread. -) - -// String returns a human-readable representation of a ResourceKind. -// -// The format of the returned string is for debugging purposes and is subject to change. -func (r ResourceKind) String() string { - switch r { - case ResourceNone: - return "None" - case ResourceGoroutine: - return "Goroutine" - case ResourceProc: - return "Proc" - case ResourceThread: - return "Thread" - } - return "Bad" -} - -// ResourceID represents a generic resource ID. -type ResourceID struct { - // Kind is the kind of resource this ID is for. - Kind ResourceKind - id int64 -} - -// MakeResourceID creates a general resource ID from a specific resource's ID. -func MakeResourceID[T interface{ GoID | ProcID | ThreadID }](id T) ResourceID { - var rd ResourceID - var a any = id - switch a.(type) { - case GoID: - rd.Kind = ResourceGoroutine - case ProcID: - rd.Kind = ResourceProc - case ThreadID: - rd.Kind = ResourceThread - } - rd.id = int64(id) - return rd -} - -// Goroutine obtains a GoID from the resource ID. -// -// r.Kind must be ResourceGoroutine or this function will panic. -func (r ResourceID) Goroutine() GoID { - if r.Kind != ResourceGoroutine { - panic(fmt.Sprintf("attempted to get GoID from %s resource ID", r.Kind)) - } - return GoID(r.id) -} - -// Proc obtains a ProcID from the resource ID. -// -// r.Kind must be ResourceProc or this function will panic. -func (r ResourceID) Proc() ProcID { - if r.Kind != ResourceProc { - panic(fmt.Sprintf("attempted to get ProcID from %s resource ID", r.Kind)) - } - return ProcID(r.id) -} - -// Thread obtains a ThreadID from the resource ID. -// -// r.Kind must be ResourceThread or this function will panic. -func (r ResourceID) Thread() ThreadID { - if r.Kind != ResourceThread { - panic(fmt.Sprintf("attempted to get ThreadID from %s resource ID", r.Kind)) - } - return ThreadID(r.id) -} - -// String returns a human-readable string representation of the ResourceID. -// -// This representation is subject to change and is intended primarily for debugging. -func (r ResourceID) String() string { - if r.Kind == ResourceNone { - return r.Kind.String() - } - return fmt.Sprintf("%s(%d)", r.Kind, r.id) -} - -// StateTransition provides details about a StateTransition event. -type StateTransition struct { - // Resource is the resource this state transition is for. - Resource ResourceID - - // Reason is a human-readable reason for the state transition. - Reason string - - // Stack is the stack trace of the resource making the state transition. - // - // This is distinct from the result (Event).Stack because it pertains to - // the transitioning resource, not any of the ones executing the event - // this StateTransition came from. - // - // An example of this difference is the NotExist -> Runnable transition for - // goroutines, which indicates goroutine creation. In this particular case, - // a Stack here would refer to the starting stack of the new goroutine, and - // an (Event).Stack would refer to the stack trace of whoever created the - // goroutine. - Stack Stack - - // The actual transition data. Stored in a neutral form so that - // we don't need fields for every kind of resource. - id int64 - oldState uint8 - newState uint8 -} - -func goStateTransition(id GoID, from, to GoState) StateTransition { - return StateTransition{ - Resource: ResourceID{Kind: ResourceGoroutine, id: int64(id)}, - oldState: uint8(from), - newState: uint8(to), - } -} - -func procStateTransition(id ProcID, from, to ProcState) StateTransition { - return StateTransition{ - Resource: ResourceID{Kind: ResourceProc, id: int64(id)}, - oldState: uint8(from), - newState: uint8(to), - } -} - -// Goroutine returns the state transition for a goroutine. -// -// Transitions to and from states that are Executing are special in that -// they change the future execution context. In other words, future events -// on the same thread will feature the same goroutine until it stops running. -// -// Panics if d.Resource.Kind is not ResourceGoroutine. -func (d StateTransition) Goroutine() (from, to GoState) { - if d.Resource.Kind != ResourceGoroutine { - panic("Goroutine called on non-Goroutine state transition") - } - return GoState(d.oldState), GoState(d.newState) -} - -// Proc returns the state transition for a proc. -// -// Transitions to and from states that are Executing are special in that -// they change the future execution context. In other words, future events -// on the same thread will feature the same goroutine until it stops running. -// -// Panics if d.Resource.Kind is not ResourceProc. -func (d StateTransition) Proc() (from, to ProcState) { - if d.Resource.Kind != ResourceProc { - panic("Proc called on non-Proc state transition") - } - return ProcState(d.oldState), ProcState(d.newState) -} diff --git a/src/internal/trace/v2/testdata/README.md b/src/internal/trace/v2/testdata/README.md deleted file mode 100644 index 0fae9caf37..0000000000 --- a/src/internal/trace/v2/testdata/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Trace test data - -## Trace golden tests - -Trace tests can be generated by running - -``` -go generate . -``` - -with the relevant toolchain in this directory. - -This will put the tests into a `tests` directory where the trace reader -tests will find them. - -A subset of tests can be regenerated by specifying a regexp pattern for -the names of tests to generate in the `GOTRACETEST` environment -variable. -Test names are defined as the name of the `.go` file that generates the -trace, but with the `.go` extension removed. - -## Trace test programs - -The trace test programs in the `testprog` directory generate traces to -stdout. -Otherwise they're just normal programs. - -## Trace debug commands - -The `cmd` directory contains helpful tools for debugging traces. - -* `gotraceraw` parses traces without validation. - It can produce a text version of the trace wire format, or convert - the text format back into bytes. -* `gotracevalidate` parses traces and validates them. - It performs more rigorous checks than the parser does on its own, - which helps for debugging the parser as well. - In fact, it performs the exact same checks that the tests do. diff --git a/src/internal/trace/v2/testdata/cmd/gotraceeventstats/main.go b/src/internal/trace/v2/testdata/cmd/gotraceeventstats/main.go deleted file mode 100644 index ad06af6481..0000000000 --- a/src/internal/trace/v2/testdata/cmd/gotraceeventstats/main.go +++ /dev/null @@ -1,136 +0,0 @@ -// 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. - -package main - -import ( - "cmp" - "encoding/binary" - "flag" - "fmt" - "io" - "log" - "os" - "slices" - "text/tabwriter" - - "internal/trace/v2/event" - "internal/trace/v2/raw" -) - -func init() { - flag.Usage = func() { - fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin.\n") - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:") - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "* size - dumps size stats\n") - fmt.Fprintf(flag.CommandLine.Output(), "\n") - flag.PrintDefaults() - } - log.SetFlags(0) -} - -func main() { - log.SetPrefix("") - flag.Parse() - - if flag.NArg() != 1 { - log.Print("missing mode argument") - flag.Usage() - os.Exit(1) - } - var err error - switch mode := flag.Arg(0); mode { - case "size": - err = printSizeStats(os.Stdin) - default: - log.Printf("unknown mode %q", mode) - flag.Usage() - os.Exit(1) - } - if err != nil { - log.Fatalf("error: %v", err) - os.Exit(1) - } -} - -func printSizeStats(r io.Reader) error { - cr := countingReader{Reader: r} - tr, err := raw.NewReader(&cr) - if err != nil { - return err - } - type eventStats struct { - typ event.Type - count int - bytes int - } - var stats [256]eventStats - for i := range stats { - stats[i].typ = event.Type(i) - } - eventsRead := 0 - for { - e, err := tr.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - return err - } - s := &stats[e.Ev] - s.count++ - s.bytes += encodedSize(&e) - eventsRead++ - } - slices.SortFunc(stats[:], func(a, b eventStats) int { - return cmp.Compare(b.bytes, a.bytes) - }) - specs := tr.Version().Specs() - w := tabwriter.NewWriter(os.Stdout, 3, 8, 2, ' ', 0) - fmt.Fprintf(w, "Event\tBytes\t%%\tCount\t%%\n") - fmt.Fprintf(w, "-\t-\t-\t-\t-\n") - for i := range stats { - stat := &stats[i] - name := "" - if int(stat.typ) >= len(specs) { - name = fmt.Sprintf("", stat.typ) - } else { - name = specs[stat.typ].Name - } - bytesPct := float64(stat.bytes) / float64(cr.bytesRead) * 100 - countPct := float64(stat.count) / float64(eventsRead) * 100 - fmt.Fprintf(w, "%s\t%d\t%.2f%%\t%d\t%.2f%%\n", name, stat.bytes, bytesPct, stat.count, countPct) - } - w.Flush() - return nil -} - -func encodedSize(e *raw.Event) int { - size := 1 - var buf [binary.MaxVarintLen64]byte - for _, arg := range e.Args { - size += binary.PutUvarint(buf[:], arg) - } - spec := e.Version.Specs()[e.Ev] - if spec.HasData { - size += binary.PutUvarint(buf[:], uint64(len(e.Data))) - size += len(e.Data) - } - return size -} - -type countingReader struct { - io.Reader - bytesRead int -} - -func (r *countingReader) Read(b []byte) (int, error) { - n, err := r.Reader.Read(b) - r.bytesRead += n - return n, err -} diff --git a/src/internal/trace/v2/testdata/cmd/gotraceraw/main.go b/src/internal/trace/v2/testdata/cmd/gotraceraw/main.go deleted file mode 100644 index 3df11be7a8..0000000000 --- a/src/internal/trace/v2/testdata/cmd/gotraceraw/main.go +++ /dev/null @@ -1,86 +0,0 @@ -// 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. - -package main - -import ( - "flag" - "fmt" - "io" - "log" - "os" - - "internal/trace/v2/raw" - "internal/trace/v2/version" -) - -func init() { - flag.Usage = func() { - fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:") - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "* text2bytes - converts a text format trace to bytes\n") - fmt.Fprintf(flag.CommandLine.Output(), "* bytes2text - converts a byte format trace to text\n") - fmt.Fprintf(flag.CommandLine.Output(), "\n") - flag.PrintDefaults() - } - log.SetFlags(0) -} - -func main() { - flag.Parse() - if narg := flag.NArg(); narg != 1 { - log.Fatal("expected exactly one positional argument: the mode to operate in; see -h output") - } - - r := os.Stdin - w := os.Stdout - - var tr traceReader - var tw traceWriter - var err error - - switch flag.Arg(0) { - case "text2bytes": - tr, err = raw.NewTextReader(r) - if err != nil { - log.Fatal(err) - } - tw, err = raw.NewWriter(w, tr.Version()) - if err != nil { - log.Fatal(err) - } - case "bytes2text": - tr, err = raw.NewReader(r) - if err != nil { - log.Fatal(err) - } - tw, err = raw.NewTextWriter(w, tr.Version()) - if err != nil { - log.Fatal(err) - } - } - for { - ev, err := tr.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - log.Fatal(err) - } - if err := tw.WriteEvent(ev); err != nil { - log.Fatal(err) - } - } -} - -type traceReader interface { - Version() version.Version - ReadEvent() (raw.Event, error) -} - -type traceWriter interface { - WriteEvent(raw.Event) error -} diff --git a/src/internal/trace/v2/testdata/cmd/gotracevalidate/main.go b/src/internal/trace/v2/testdata/cmd/gotracevalidate/main.go deleted file mode 100644 index 944d19f85e..0000000000 --- a/src/internal/trace/v2/testdata/cmd/gotracevalidate/main.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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. - -package main - -import ( - "flag" - "fmt" - "io" - "log" - "os" - - "internal/trace/v2" - "internal/trace/v2/testtrace" -) - -func init() { - flag.Usage = func() { - fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin and validates it.\n") - flag.PrintDefaults() - } - log.SetFlags(0) -} - -var logEvents = flag.Bool("log-events", false, "whether to log events") - -func main() { - flag.Parse() - - r, err := trace.NewReader(os.Stdin) - if err != nil { - log.Fatal(err) - } - v := testtrace.NewValidator() - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - log.Fatal(err) - } - if *logEvents { - log.Println(ev.String()) - } - if err := v.Event(ev); err != nil { - log.Fatal(err) - } - } -} diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/0cb1786dee0f090b b/src/internal/trace/v2/testdata/fuzz/FuzzReader/0cb1786dee0f090b deleted file mode 100644 index 326ebe1c6e..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/0cb1786dee0f090b +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x190000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/1e45307d5b2ec36d b/src/internal/trace/v2/testdata/fuzz/FuzzReader/1e45307d5b2ec36d deleted file mode 100644 index 406af9caa6..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/1e45307d5b2ec36d +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0001") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/2b05796f9b2fc48d b/src/internal/trace/v2/testdata/fuzz/FuzzReader/2b05796f9b2fc48d deleted file mode 100644 index 50fdccda6b..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/2b05796f9b2fc48d +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00-0000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/2b9be9aebe08d511 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/2b9be9aebe08d511 deleted file mode 100644 index 6bcb99adfc..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/2b9be9aebe08d511 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x0f00\x120\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/344331b314da0b08 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/344331b314da0b08 deleted file mode 100644 index de6e4694be..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/344331b314da0b08 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\b0000\x01\x01\xff00\xb8\x00\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1901\xff\xff\xff\xff\xff\xff\xff\xff0\x800") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/365d7b5b633b3f97 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/365d7b5b633b3f97 deleted file mode 100644 index 8dc370f383..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/365d7b5b633b3f97 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x0100\x8c0\x85\x00\b0000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/4055b17cae1a3443 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/4055b17cae1a3443 deleted file mode 100644 index ea5417c78a..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/4055b17cae1a3443 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.11 trace\x00\x00\x00A00\x020$0") diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/4d9ddc909984e871 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/4d9ddc909984e871 deleted file mode 100644 index 040b2a4cae..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/4d9ddc909984e871 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x11\r\xa700\x01\x19000\x02$000000\x01\x0100\x05\b0000\x01\x0110\x11\r\xa700\x01\x19 00\x02\x110 0000") diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/56f073e57903588c b/src/internal/trace/v2/testdata/fuzz/FuzzReader/56f073e57903588c deleted file mode 100644 index d34fe3f06c..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/56f073e57903588c +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x1f0000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 deleted file mode 100644 index 5677261155..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x11\r\xa700\x01\x19000\x02#000000\x01\x0100\x05\b0000\x01\x0110\x11\r\xa700\x01\x19 00\x02\x110 0000") diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/aeb749b6bc317b66 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/aeb749b6bc317b66 deleted file mode 100644 index f93b5a90da..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/aeb749b6bc317b66 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/closing-unknown-region b/src/internal/trace/v2/testdata/fuzz/FuzzReader/closing-unknown-region deleted file mode 100644 index 7433214030..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/closing-unknown-region +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xf6\x9f\n\x9fÕ\xb4\x99\xb2\x06\x11\r\xa7\x02\x00\x01\x19\x05\x01\xf6\x9f\n\x02+\x04\x01\x00\x00") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/d478e18d2d6756b7 b/src/internal/trace/v2/testdata/fuzz/FuzzReader/d478e18d2d6756b7 deleted file mode 100644 index 3e5fda833a..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/d478e18d2d6756b7 +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\"0000\x01\x0100\x88\x00\b0000000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/d91203cd397aa0bc b/src/internal/trace/v2/testdata/fuzz/FuzzReader/d91203cd397aa0bc deleted file mode 100644 index d24b94ac97..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/d91203cd397aa0bc +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01001\x85\x00\b0000") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/invalid-proc-state b/src/internal/trace/v2/testdata/fuzz/FuzzReader/invalid-proc-state deleted file mode 100644 index e5d3258111..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/invalid-proc-state +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\x05\r\xa7\x02\x00E") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/large-id b/src/internal/trace/v2/testdata/fuzz/FuzzReader/large-id deleted file mode 100644 index 0fb6273b44..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/large-id +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\f\x02\x03\xff\xff\xff\xff\xff\xff\xff\x9f\x1d\x00") \ No newline at end of file diff --git a/src/internal/trace/v2/testdata/fuzz/FuzzReader/malformed-timestamp b/src/internal/trace/v2/testdata/fuzz/FuzzReader/malformed-timestamp deleted file mode 100644 index 850ca50f87..0000000000 --- a/src/internal/trace/v2/testdata/fuzz/FuzzReader/malformed-timestamp +++ /dev/null @@ -1,2 +0,0 @@ -go test fuzz v1 -[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xfa\x9f\n\xa5ѕ\xb4\x99\xb2\x06\x0e\n\x97\x96\x96\x96\x96\x96\x96\x96\x96\x96\x01\x01\x01") diff --git a/src/internal/trace/v2/testdata/generate.go b/src/internal/trace/v2/testdata/generate.go deleted file mode 100644 index c0658b2329..0000000000 --- a/src/internal/trace/v2/testdata/generate.go +++ /dev/null @@ -1,6 +0,0 @@ -// 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. - -//go:generate go run mktests.go -package testdata diff --git a/src/internal/trace/v2/testdata/generators/go122-confuse-seq-across-generations.go b/src/internal/trace/v2/testdata/generators/go122-confuse-seq-across-generations.go deleted file mode 100644 index f618c41e78..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-confuse-seq-across-generations.go +++ /dev/null @@ -1,62 +0,0 @@ -// 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. - -// Regression test for an issue found in development. -// -// The core of the issue is that if generation counters -// aren't considered as part of sequence numbers, then -// it's possible to accidentally advance without a -// GoStatus event. -// -// The situation is one in which it just so happens that -// an event on the frontier for a following generation -// has a sequence number exactly one higher than the last -// sequence number for e.g. a goroutine in the previous -// generation. The parser should wait to find a GoStatus -// event before advancing into the next generation at all. -// It turns out this situation is pretty rare; the GoStatus -// event almost always shows up first in practice. But it -// can and did happen. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g1 := t.Generation(1) - - // A running goroutine blocks. - b10 := g1.Batch(trace.ThreadID(0), 0) - b10.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b10.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b10.Event("GoStop", "whatever", testgen.NoStack) - - // The running goroutine gets unblocked. - b11 := g1.Batch(trace.ThreadID(1), 0) - b11.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) - b11.Event("GoStart", trace.GoID(1), testgen.Seq(1)) - b11.Event("GoStop", "whatever", testgen.NoStack) - - g2 := t.Generation(2) - - // Start running the goroutine, but later. - b21 := g2.Batch(trace.ThreadID(1), 3) - b21.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) - b21.Event("GoStart", trace.GoID(1), testgen.Seq(2)) - - // The goroutine starts running, then stops, then starts again. - b20 := g2.Batch(trace.ThreadID(0), 5) - b20.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b20.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunnable) - b20.Event("GoStart", trace.GoID(1), testgen.Seq(1)) - b20.Event("GoStop", "whatever", testgen.NoStack) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-create-syscall-reuse-thread-id.go b/src/internal/trace/v2/testdata/generators/go122-create-syscall-reuse-thread-id.go deleted file mode 100644 index 107cce2cc2..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-create-syscall-reuse-thread-id.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 G being created from within a syscall. -// -// Specifically, it tests a scenerio wherein a C -// thread is calling into Go, creating a goroutine in -// a syscall (in the tracer's model). The system is free -// to reuse thread IDs, so first a thread ID is used to -// call into Go, and then is used for a Go-created thread. -// -// This is a regression test. The trace parser didn't correctly -// model GoDestroySyscall as dropping its P (even if the runtime -// did). It turns out this is actually fine if all the threads -// in the trace have unique IDs, since the P just stays associated -// with an eternally dead thread, and it's stolen by some other -// thread later. But if thread IDs are reused, then the tracer -// gets confused when trying to advance events on the new thread. -// The now-dead thread which exited on a GoDestroySyscall still has -// its P associated and this transfers to the newly-live thread -// in the parser's state because they share a thread ID. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // A C thread calls into Go and acquires a P. It returns - // back to C, destroying the G. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("GoCreateSyscall", trace.GoID(4)) - b0.Event("GoSyscallEndBlocked") - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) - b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) - b0.Event("GoStatus", trace.GoID(4), trace.NoThread, go122.GoRunnable) - b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) - b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) - b0.Event("GoDestroySyscall") - - // A new Go-created thread with the same ID appears and - // starts running, then tries to steal the P from the - // first thread. The stealing is interesting because if - // the parser handles GoDestroySyscall wrong, then we - // have a self-steal here potentially that doesn't make - // sense. - b1 := g.Batch(trace.ThreadID(0), 0) - b1.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) - b1.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(3), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-create-syscall-with-p.go b/src/internal/trace/v2/testdata/generators/go122-create-syscall-with-p.go deleted file mode 100644 index 4cb1c4a9a7..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-create-syscall-with-p.go +++ /dev/null @@ -1,52 +0,0 @@ -// 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 G being created from within a syscall. -// -// Specifically, it tests a scenerio wherein a C -// thread is calling into Go, creating a goroutine in -// a syscall (in the tracer's model). Because the actual -// m can be reused, it's possible for that m to have never -// had its P (in _Psyscall) stolen if the runtime doesn't -// model the scenario correctly. Make sure we reject such -// traces. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - t.ExpectFailure(".*expected a proc but didn't have one.*") - - g := t.Generation(1) - - // A C thread calls into Go and acquires a P. It returns - // back to C, destroying the G. It then comes back to Go - // on the same thread and again returns to C. - // - // Note: on pthread platforms this can't happen on the - // same thread because the m is stashed in TLS between - // calls into Go, until the thread dies. This is still - // possible on other platforms, however. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("GoCreateSyscall", trace.GoID(4)) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) - b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) - b0.Event("GoSyscallEndBlocked") - b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) - b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) - b0.Event("GoDestroySyscall") - b0.Event("GoCreateSyscall", trace.GoID(4)) - b0.Event("GoSyscallEnd") - b0.Event("GoSyscallBegin", testgen.Seq(3), testgen.NoStack) - b0.Event("GoDestroySyscall") -} diff --git a/src/internal/trace/v2/testdata/generators/go122-fail-first-gen-first.go b/src/internal/trace/v2/testdata/generators/go122-fail-first-gen-first.go deleted file mode 100644 index e5dea24e3b..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-fail-first-gen-first.go +++ /dev/null @@ -1,44 +0,0 @@ -// 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. - -// Regression test for #55160. -// -// The issue is that the parser reads ahead to the first batch of the -// next generation to find generation boundaries, but if it finds an -// error, it needs to delay handling that error until later. Previously -// it would handle that error immediately and a totally valid generation -// would be skipped for parsing and rejected because of an error in a -// batch in the following generation. -// -// This test captures this behavior by making both the first generation -// and second generation bad. It requires that the issue in the first -// generation, which is caught when actually ordering events, be reported -// instead of the second one. - -package main - -import ( - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - // A running goroutine emits a task begin. - t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) - t.RawEvent(go122.EvFrequency, nil, 15625000) - - // A running goroutine emits a task begin. - t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) - t.RawEvent(go122.EvGoCreate, nil, 0 /*timestamp delta*/, 1 /*go ID*/, 0, 0) - - // Write an invalid batch event for the next generation. - t.RawEvent(go122.EvEventBatch, nil, 2 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 50 /*batch length (invalid)*/) - - // We should fail at the first issue, not the second one. - t.ExpectFailure("expected a proc but didn't have one") -} diff --git a/src/internal/trace/v2/testdata/generators/go122-go-create-without-running-g.go b/src/internal/trace/v2/testdata/generators/go122-go-create-without-running-g.go deleted file mode 100644 index b693245b5b..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-go-create-without-running-g.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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. - -// Regression test for an issue found in development. -// -// GoCreate events can happen on bare Ps in a variety of situations and -// and earlier version of the parser assumed this wasn't possible. At -// the time of writing, one such example is goroutines created by expiring -// timers. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g1 := t.Generation(1) - - // A goroutine gets created on a running P, then starts running. - b0 := g1.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoCreate", trace.GoID(5), testgen.NoStack, testgen.NoStack) - b0.Event("GoStart", trace.GoID(5), testgen.Seq(1)) - b0.Event("GoStop", "whatever", testgen.NoStack) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-ambiguous.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-ambiguous.go deleted file mode 100644 index 349a575ef3..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-ambiguous.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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 syscall P stealing. -// -// Specifically, it tests a scenerio wherein, without a -// P sequence number of GoSyscallBegin, the syscall that -// a ProcSteal applies to is ambiguous. This only happens in -// practice when the events aren't already properly ordered -// by timestamp, since the ProcSteal won't be seen until after -// the correct GoSyscallBegin appears on the frontier. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - t.DisableTimestamps() - - g := t.Generation(1) - - // One goroutine does a syscall without blocking, then another one where - // it's P gets stolen. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) - b0.Event("GoSyscallEnd") - b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) - b0.Event("GoSyscallEndBlocked") - - // A running goroutine steals proc 0. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(3), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go deleted file mode 100644 index f4c9f6ecf3..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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 syscall P stealing at a generation boundary. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine is exiting with a syscall. It already - // acquired a new P. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) - b0.Event("GoSyscallEndBlocked") - - // A bare M stole the goroutine's P at the generation boundary. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go deleted file mode 100644 index e6023ba701..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go +++ /dev/null @@ -1,34 +0,0 @@ -// 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 syscall P stealing at a generation boundary. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine is exiting with a syscall. It already - // acquired a new P. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) - b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) - b0.Event("GoSyscallEndBlocked") - - // A bare M stole the goroutine's P at the generation boundary. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go deleted file mode 100644 index 2232dca5dc..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go +++ /dev/null @@ -1,36 +0,0 @@ -// 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 syscall P stealing at a generation boundary. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine is exiting with a syscall. It already - // acquired a new P. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) - b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) - b0.Event("GoSyscallEndBlocked") - - // A running goroutine stole P0 at the generation boundary. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary.go deleted file mode 100644 index 710827a8f6..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-gen-boundary.go +++ /dev/null @@ -1,35 +0,0 @@ -// 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 syscall P stealing at a generation boundary. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine is exiting with a syscall. It already - // acquired a new P. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) - b0.Event("GoSyscallEndBlocked") - - // A running goroutine stole P0 at the generation boundary. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go deleted file mode 100644 index 24e5cb2a3e..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go +++ /dev/null @@ -1,34 +0,0 @@ -// 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 syscall P stealing. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine enters a syscall, grabs a P, and starts running. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) - b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) - b0.Event("GoSyscallEndBlocked") - - // A bare M steals the goroutine's P. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go deleted file mode 100644 index 2caefe8be5..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go +++ /dev/null @@ -1,36 +0,0 @@ -// 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 syscall P stealing. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine enters a syscall, grabs a P, and starts running. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) - b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) - b0.Event("GoSyscallEndBlocked") - - // A running goroutine steals proc 0. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-self.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-self.go deleted file mode 100644 index dd947346c6..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-self.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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 syscall P stealing. -// -// Specifically, it tests a scenario where a thread 'steals' -// a P from itself. It's just a ProcStop with extra steps when -// it happens on the same P. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - t.DisableTimestamps() - - g := t.Generation(1) - - // A goroutine execute a syscall and steals its own P, then starts running - // on that P. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) - b0.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) - b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(3)) - b0.Event("GoSyscallEndBlocked") -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go deleted file mode 100644 index 630eba8cf2..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go +++ /dev/null @@ -1,32 +0,0 @@ -// 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 syscall P stealing. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine enters a syscall. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) - b0.Event("GoSyscallEndBlocked") - - // A bare M steals the goroutine's P. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple.go deleted file mode 100644 index 54b43f4f0b..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-simple.go +++ /dev/null @@ -1,34 +0,0 @@ -// 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 syscall P stealing. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // One goroutine enters a syscall. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) - b0.Event("GoSyscallEndBlocked") - - // A running goroutine steals proc 0. - b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) - b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go deleted file mode 100644 index 870f8f69f6..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go +++ /dev/null @@ -1,32 +0,0 @@ -// 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 syscall P stealing from a goroutine and thread -// that have been in a syscall the entire generation. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g := t.Generation(1) - - // Steal proc from a goroutine that's been blocked - // in a syscall the entire generation. - b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) - b0.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(1)) - - // Status event for a goroutine blocked in a syscall for the entire generation. - bz := g.Batch(trace.NoThread, 0) - bz.Event("GoStatus", trace.GoID(1), trace.ThreadID(1), go122.GoSyscall) -} diff --git a/src/internal/trace/v2/testdata/generators/go122-task-across-generations.go b/src/internal/trace/v2/testdata/generators/go122-task-across-generations.go deleted file mode 100644 index 06ef96e51a..0000000000 --- a/src/internal/trace/v2/testdata/generators/go122-task-across-generations.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -// Regression test for an issue found in development. -// -// The issue is that EvUserTaskEnd events don't carry the -// task type with them, so the parser needs to track that -// information. But if the parser just tracks the string ID -// and not the string itself, that string ID may not be valid -// for use in future generations. - -package main - -import ( - "internal/trace/v2" - "internal/trace/v2/event/go122" - testgen "internal/trace/v2/internal/testgen/go122" -) - -func main() { - testgen.Main(gen) -} - -func gen(t *testgen.Trace) { - g1 := t.Generation(1) - - // A running goroutine emits a task begin. - b1 := g1.Batch(trace.ThreadID(0), 0) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b1.Event("UserTaskBegin", trace.TaskID(2), trace.TaskID(0) /* 0 means no parent, not background */, "my task", testgen.NoStack) - - g2 := t.Generation(2) - - // That same goroutine emits a task end in the following generation. - b2 := g2.Batch(trace.ThreadID(0), 5) - b2.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b2.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b2.Event("UserTaskEnd", trace.TaskID(2), testgen.NoStack) -} diff --git a/src/internal/trace/v2/testdata/mktests.go b/src/internal/trace/v2/testdata/mktests.go deleted file mode 100644 index 57901d96a3..0000000000 --- a/src/internal/trace/v2/testdata/mktests.go +++ /dev/null @@ -1,160 +0,0 @@ -// 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. - -//go:build ignore - -package main - -import ( - "bytes" - "fmt" - "internal/trace/v2/raw" - "internal/trace/v2/version" - "internal/txtar" - "io" - "log" - "os" - "os/exec" - "path/filepath" - "regexp" -) - -func main() { - log.SetFlags(0) - ctx, err := newContext() - if err != nil { - log.Fatal(err) - } - if err := ctx.runGenerators(); err != nil { - log.Fatal(err) - } - if err := ctx.runTestProg("./testprog/annotations.go"); err != nil { - log.Fatal(err) - } - if err := ctx.runTestProg("./testprog/annotations-stress.go"); err != nil { - log.Fatal(err) - } -} - -type context struct { - testNames map[string]struct{} - filter *regexp.Regexp -} - -func newContext() (*context, error) { - var filter *regexp.Regexp - var err error - if pattern := os.Getenv("GOTRACETEST"); pattern != "" { - filter, err = regexp.Compile(pattern) - if err != nil { - return nil, fmt.Errorf("compiling regexp %q for GOTRACETEST: %v", pattern, err) - } - } - return &context{ - testNames: make(map[string]struct{}), - filter: filter, - }, nil -} - -func (ctx *context) register(testName string) (skip bool, err error) { - if _, ok := ctx.testNames[testName]; ok { - return true, fmt.Errorf("duplicate test %s found", testName) - } - if ctx.filter != nil { - return !ctx.filter.MatchString(testName), nil - } - return false, nil -} - -func (ctx *context) runGenerators() error { - generators, err := filepath.Glob("./generators/*.go") - if err != nil { - return fmt.Errorf("reading generators: %v", err) - } - genroot := "./tests" - - if err := os.MkdirAll(genroot, 0777); err != nil { - return fmt.Errorf("creating generated root: %v", err) - } - for _, path := range generators { - name := filepath.Base(path) - name = name[:len(name)-len(filepath.Ext(name))] - - // Skip if we have a pattern and this test doesn't match. - skip, err := ctx.register(name) - if err != nil { - return err - } - if skip { - continue - } - - fmt.Fprintf(os.Stderr, "generating %s... ", name) - - // Get the test path. - testPath := filepath.Join(genroot, fmt.Sprintf("%s.test", name)) - - // Run generator. - cmd := exec.Command("go", "run", path, testPath) - if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("running generator %s: %v:\n%s", name, err, out) - } - fmt.Fprintln(os.Stderr) - } - return nil -} - -func (ctx *context) runTestProg(progPath string) error { - name := filepath.Base(progPath) - name = name[:len(name)-len(filepath.Ext(name))] - name = fmt.Sprintf("go1%d-%s", version.Current, name) - - // Skip if we have a pattern and this test doesn't match. - skip, err := ctx.register(name) - if err != nil { - return err - } - if skip { - return nil - } - - // Create command. - var trace, stderr bytes.Buffer - cmd := exec.Command("go", "run", progPath) - cmd.Stdout = &trace - cmd.Stderr = &stderr - - // Run trace program; the trace will appear in stdout. - fmt.Fprintf(os.Stderr, "running trace program %s...\n", name) - if err := cmd.Run(); err != nil { - log.Fatalf("running trace program: %v:\n%s", err, stderr.String()) - } - - // Write out the trace. - var textTrace bytes.Buffer - r, err := raw.NewReader(&trace) - if err != nil { - log.Fatalf("reading trace: %v", err) - } - w, err := raw.NewTextWriter(&textTrace, version.Current) - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - log.Fatalf("reading trace: %v", err) - } - if err := w.WriteEvent(ev); err != nil { - log.Fatalf("writing trace: %v", err) - } - } - testData := txtar.Format(&txtar.Archive{ - Files: []txtar.File{ - {Name: "expect", Data: []byte("SUCCESS")}, - {Name: "trace", Data: textTrace.Bytes()}, - }, - }) - return os.WriteFile(fmt.Sprintf("./tests/%s.test", name), testData, 0o664) -} diff --git a/src/internal/trace/v2/testdata/testprog/annotations-stress.go b/src/internal/trace/v2/testdata/testprog/annotations-stress.go deleted file mode 100644 index 511d6ed890..0000000000 --- a/src/internal/trace/v2/testdata/testprog/annotations-stress.go +++ /dev/null @@ -1,84 +0,0 @@ -// 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 deleted file mode 100644 index 2507bc4d38..0000000000 --- a/src/internal/trace/v2/testdata/testprog/annotations.go +++ /dev/null @@ -1,60 +0,0 @@ -// 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 deleted file mode 100644 index d63650047e..0000000000 --- a/src/internal/trace/v2/testdata/testprog/cgo-callback.go +++ /dev/null @@ -1,80 +0,0 @@ -// 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 - -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 deleted file mode 100644 index 293a2acac8..0000000000 --- a/src/internal/trace/v2/testdata/testprog/cpu-profile.go +++ /dev/null @@ -1,137 +0,0 @@ -// 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 deleted file mode 100644 index cc489818fc..0000000000 --- a/src/internal/trace/v2/testdata/testprog/futile-wakeup.go +++ /dev/null @@ -1,84 +0,0 @@ -// 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 deleted file mode 100644 index 7979234c40..0000000000 --- a/src/internal/trace/v2/testdata/testprog/gc-stress.go +++ /dev/null @@ -1,80 +0,0 @@ -// 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" - "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]*[1024]*node -var sink [][]byte - -func main() { - for i := range trees { - trees[i] = makeTree(6) - } - ballast = new([16]*[1024]*node) - for i := range ballast { - ballast[i] = new([1024]*node) - for j := range ballast[i] { - ballast[i][j] = &node{ - data: [128]byte{1, 2, 3, 4}, - } - } - } - - procs := runtime.GOMAXPROCS(-1) - sink = make([][]byte, procs) - - for i := 0; i < procs; i++ { - i := i - go func() { - for { - sink[i] = make([]byte, 4<<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 deleted file mode 100644 index 265120767d..0000000000 --- a/src/internal/trace/v2/testdata/testprog/gomaxprocs.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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/iter-pull.go b/src/internal/trace/v2/testdata/testprog/iter-pull.go deleted file mode 100644 index ba8f41365e..0000000000 --- a/src/internal/trace/v2/testdata/testprog/iter-pull.go +++ /dev/null @@ -1,85 +0,0 @@ -// 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 coroutine switches. - -//go:build ignore - -package main - -import ( - "iter" - "log" - "os" - "runtime/trace" - "sync" -) - -func main() { - // Start tracing. - if err := trace.Start(os.Stdout); err != nil { - log.Fatalf("failed to start tracing: %v", err) - } - - // Try simple pull iteration. - i := pullRange(100) - for { - _, ok := i.next() - if !ok { - break - } - } - - // Try bouncing the pull iterator between two goroutines. - var wg sync.WaitGroup - var iterChans [2]chan intIter - wg.Add(2) - iterChans[0] = make(chan intIter) - iterChans[1] = make(chan intIter) - go func() { - defer wg.Done() - - iter := pullRange(100) - iterChans[1] <- iter - - for i := range iterChans[0] { - _, ok := i.next() - if !ok { - close(iterChans[1]) - break - } - iterChans[1] <- i - } - }() - go func() { - defer wg.Done() - - for i := range iterChans[1] { - _, ok := i.next() - if !ok { - close(iterChans[0]) - break - } - iterChans[0] <- i - } - }() - wg.Wait() - - // End of traced execution. - trace.Stop() -} - -func pullRange(n int) intIter { - next, stop := iter.Pull(func(yield func(v int) bool) { - for i := range n { - yield(i) - } - }) - return intIter{next: next, stop: stop} -} - -type intIter struct { - next func() (int, bool) - stop func() -} diff --git a/src/internal/trace/v2/testdata/testprog/many-start-stop.go b/src/internal/trace/v2/testdata/testprog/many-start-stop.go deleted file mode 100644 index 2d5d0634f0..0000000000 --- a/src/internal/trace/v2/testdata/testprog/many-start-stop.go +++ /dev/null @@ -1,38 +0,0 @@ -// 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 deleted file mode 100644 index e64bc86844..0000000000 --- a/src/internal/trace/v2/testdata/testprog/stacks.go +++ /dev/null @@ -1,129 +0,0 @@ -// 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 deleted file mode 100644 index 72c1c59417..0000000000 --- a/src/internal/trace/v2/testdata/testprog/stress-start-stop.go +++ /dev/null @@ -1,166 +0,0 @@ -// 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 deleted file mode 100644 index 99696d1756..0000000000 --- a/src/internal/trace/v2/testdata/testprog/stress.go +++ /dev/null @@ -1,146 +0,0 @@ -// 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 deleted file mode 100644 index 912f5dd3bc..0000000000 --- a/src/internal/trace/v2/testdata/testprog/wait-on-pipe.go +++ /dev/null @@ -1,66 +0,0 @@ -// 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() -} diff --git a/src/internal/trace/v2/testdata/tests/go122-annotations-stress.test b/src/internal/trace/v2/testdata/tests/go122-annotations-stress.test deleted file mode 100644 index 8da8c0f318..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-annotations-stress.test +++ /dev/null @@ -1,1179 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=18446744073709551615 time=2753926854385 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=1986497 time=2753925247434 size=1430 -ProcStart dt=336 p=2 p_seq=1 -GoStart dt=191 g=19 g_seq=1 -HeapAlloc dt=389 heapalloc_value=1622016 -HeapAlloc dt=4453 heapalloc_value=1662976 -GoBlock dt=572 reason_string=12 stack=29 -ProcStop dt=26 -ProcStart dt=160734 p=2 p_seq=2 -ProcStop dt=21 -ProcStart dt=159292 p=0 p_seq=7 -GoStart dt=299 g=49 g_seq=1 -UserRegionBegin dt=183 task=8 name_string=33 stack=26 -UserLog dt=26 task=8 key_string=24 value_string=49 stack=27 -UserRegionEnd dt=8 task=8 name_string=33 stack=28 -GoDestroy dt=3 -GoStart dt=20 g=50 g_seq=1 -UserRegionBegin dt=40 task=8 name_string=35 stack=26 -UserLog dt=9 task=8 key_string=24 value_string=50 stack=27 -UserRegionEnd dt=2 task=8 name_string=35 stack=28 -GoDestroy dt=1 -ProcStop dt=18 -ProcStart dt=141801 p=4 p_seq=5 -ProcStop dt=18 -ProcStart dt=16860 p=4 p_seq=6 -GoUnblock dt=53 g=1 g_seq=5 stack=0 -GoUnblock dt=9 g=51 g_seq=3 stack=0 -GoStart dt=162 g=51 g_seq=4 -UserTaskEnd dt=35 task=9 stack=36 -UserRegionEnd dt=16 task=8 name_string=31 stack=28 -GoDestroy dt=2 -GoStart dt=20 g=1 g_seq=6 -UserTaskEnd dt=14 task=8 stack=54 -UserLog dt=26 task=3 key_string=24 value_string=51 stack=55 -UserTaskBegin dt=14 task=10 parent_task=3 name_string=26 stack=56 -UserLog dt=42 task=10 key_string=27 value_string=52 stack=57 -UserRegionBegin dt=12 task=10 name_string=29 stack=58 -GoCreate dt=36 new_g=35 new_stack=17 stack=59 -GoCreate dt=11 new_g=36 new_stack=17 stack=59 -GoCreate dt=18 new_g=37 new_stack=17 stack=59 -GoCreate dt=10 new_g=38 new_stack=17 stack=59 -GoCreate dt=6 new_g=39 new_stack=17 stack=59 -GoCreate dt=8 new_g=40 new_stack=17 stack=59 -UserRegionEnd dt=7 task=10 name_string=29 stack=60 -GoBlock dt=9 reason_string=19 stack=61 -GoStart dt=15 g=40 g_seq=1 -UserRegionBegin dt=110 task=10 name_string=53 stack=26 -UserLog dt=16 task=10 key_string=24 value_string=54 stack=27 -UserRegionEnd dt=2 task=10 name_string=53 stack=28 -GoDestroy dt=2 -GoStart dt=6 g=38 g_seq=1 -UserRegionBegin dt=31 task=10 name_string=30 stack=26 -UserLog dt=5 task=10 key_string=24 value_string=55 stack=27 -UserRegionEnd dt=2 task=10 name_string=30 stack=28 -GoDestroy dt=1 -GoStart dt=2 g=39 g_seq=1 -UserRegionBegin dt=23 task=10 name_string=56 stack=26 -UserLog dt=6 task=10 key_string=24 value_string=57 stack=27 -UserRegionEnd dt=1 task=10 name_string=56 stack=28 -GoDestroy dt=1 -GoStart dt=8 g=35 g_seq=1 -UserRegionBegin dt=17 task=10 name_string=33 stack=26 -UserLog dt=4 task=10 key_string=24 value_string=58 stack=27 -UserRegionEnd dt=2 task=10 name_string=33 stack=28 -GoDestroy dt=1 -GoStart dt=3 g=36 g_seq=1 -UserRegionBegin dt=19 task=10 name_string=35 stack=26 -UserLog dt=4 task=10 key_string=24 value_string=59 stack=27 -UserRegionEnd dt=2 task=10 name_string=35 stack=28 -GoDestroy dt=1 -ProcStop dt=11 -ProcStart dt=142205 p=0 p_seq=9 -ProcStop dt=19 -ProcStart dt=16811 p=0 p_seq=10 -GoUnblock dt=26 g=1 g_seq=7 stack=0 -GoStart dt=201 g=1 g_seq=8 -UserTaskEnd dt=24 task=10 stack=62 -UserLog dt=18 task=4 key_string=24 value_string=63 stack=63 -UserTaskBegin dt=11 task=12 parent_task=4 name_string=26 stack=64 -UserLog dt=21 task=12 key_string=27 value_string=64 stack=65 -UserRegionBegin dt=7 task=12 name_string=29 stack=66 -GoCreate dt=33 new_g=5 new_stack=17 stack=67 -GoCreate dt=12 new_g=6 new_stack=17 stack=67 -GoCreate dt=9 new_g=7 new_stack=17 stack=67 -GoCreate dt=8 new_g=8 new_stack=17 stack=67 -GoCreate dt=19 new_g=9 new_stack=17 stack=67 -UserRegionEnd dt=14 task=12 name_string=29 stack=68 -GoBlock dt=11 reason_string=19 stack=69 -GoStart dt=13 g=9 g_seq=1 -UserRegionBegin dt=70 task=12 name_string=56 stack=26 -UserLog dt=11 task=12 key_string=24 value_string=65 stack=27 -UserRegionEnd dt=3 task=12 name_string=56 stack=28 -GoDestroy dt=2 -GoStart dt=7 g=5 g_seq=1 -UserRegionBegin dt=24 task=12 name_string=33 stack=26 -UserLog dt=5 task=12 key_string=24 value_string=66 stack=27 -UserRegionEnd dt=2 task=12 name_string=33 stack=28 -GoDestroy dt=2 -GoStart dt=8 g=6 g_seq=1 -UserRegionBegin dt=15 task=12 name_string=35 stack=26 -UserLog dt=7 task=12 key_string=24 value_string=67 stack=27 -UserRegionEnd dt=2 task=12 name_string=35 stack=28 -GoDestroy dt=1 -GoStart dt=2 g=7 g_seq=1 -UserRegionBegin dt=13 task=12 name_string=31 stack=26 -UserLog dt=5 task=12 key_string=24 value_string=68 stack=27 -UserLog dt=6 task=12 key_string=24 value_string=69 stack=30 -UserTaskBegin dt=5 task=13 parent_task=12 name_string=26 stack=31 -UserLog dt=7 task=13 key_string=27 value_string=70 stack=32 -UserRegionBegin dt=4 task=13 name_string=29 stack=33 -UserRegionEnd dt=6 task=13 name_string=29 stack=34 -GoBlock dt=18 reason_string=19 stack=35 -GoStart dt=12 g=8 g_seq=1 -UserRegionBegin dt=22 task=12 name_string=30 stack=26 -UserLog dt=5 task=12 key_string=24 value_string=71 stack=27 -UserRegionEnd dt=2 task=12 name_string=30 stack=28 -GoDestroy dt=1 -ProcStop dt=20 -ProcStart dt=141838 p=4 p_seq=8 -ProcStop dt=16 -ProcStart dt=17652 p=4 p_seq=9 -GoUnblock dt=48 g=1 g_seq=9 stack=0 -GoUnblock dt=8 g=7 g_seq=2 stack=0 -GoStart dt=271 g=7 g_seq=3 -UserTaskEnd dt=25 task=13 stack=36 -UserRegionEnd dt=15 task=12 name_string=31 stack=28 -GoDestroy dt=4 -GoStart dt=19 g=1 g_seq=10 -UserTaskEnd dt=19 task=12 stack=70 -UserLog dt=21 task=0 key_string=24 value_string=72 stack=13 -UserTaskBegin dt=19 task=14 parent_task=0 name_string=26 stack=14 -UserLog dt=37 task=14 key_string=27 value_string=73 stack=15 -UserRegionBegin dt=6 task=14 name_string=29 stack=16 -GoCreate dt=28 new_g=41 new_stack=17 stack=18 -GoCreate dt=14 new_g=42 new_stack=17 stack=18 -GoCreate dt=12 new_g=43 new_stack=17 stack=18 -GoCreate dt=10 new_g=44 new_stack=17 stack=18 -UserRegionEnd dt=5 task=14 name_string=29 stack=19 -GoBlock dt=9 reason_string=19 stack=20 -GoStart dt=16 g=44 g_seq=1 -UserRegionBegin dt=107 task=14 name_string=30 stack=26 -UserLog dt=16 task=14 key_string=24 value_string=74 stack=27 -UserRegionEnd dt=3 task=14 name_string=30 stack=28 -GoDestroy dt=2 -GoStart dt=7 g=41 g_seq=1 -UserRegionBegin dt=30 task=14 name_string=33 stack=26 -UserLog dt=7 task=14 key_string=24 value_string=75 stack=27 -UserRegionEnd dt=2 task=14 name_string=33 stack=28 -GoDestroy dt=2 -GoStart dt=7 g=42 g_seq=1 -UserRegionBegin dt=27 task=14 name_string=35 stack=26 -UserLog dt=7 task=14 key_string=24 value_string=76 stack=27 -UserRegionEnd dt=2 task=14 name_string=35 stack=28 -GoDestroy dt=2 -ProcStop dt=28 -ProcStart dt=141923 p=0 p_seq=12 -ProcStop dt=19 -ProcStart dt=16780 p=0 p_seq=13 -GoUnblock dt=22 g=43 g_seq=2 stack=0 -GoStart dt=162 g=43 g_seq=3 -UserTaskEnd dt=16 task=15 stack=36 -UserRegionEnd dt=12 task=14 name_string=31 stack=28 -GoDestroy dt=2 -ProcStop dt=8 -ProcStart dt=1532 p=2 p_seq=9 -ProcStop dt=12 -ProcStart dt=141906 p=4 p_seq=11 -ProcStop dt=16 -ProcStart dt=16784 p=4 p_seq=12 -GoUnblock dt=20 g=1 g_seq=13 stack=0 -GoStart dt=191 g=1 g_seq=14 -UserTaskEnd dt=15 task=16 stack=45 -UserLog dt=17 task=2 key_string=24 value_string=84 stack=46 -UserTaskBegin dt=8 task=17 parent_task=2 name_string=26 stack=47 -UserLog dt=20 task=17 key_string=27 value_string=85 stack=48 -UserRegionBegin dt=6 task=17 name_string=29 stack=49 -GoCreate dt=28 new_g=45 new_stack=17 stack=50 -GoCreate dt=9 new_g=46 new_stack=17 stack=50 -GoCreate dt=10 new_g=47 new_stack=17 stack=50 -UserRegionEnd dt=5 task=17 name_string=29 stack=51 -GoBlock dt=6 reason_string=19 stack=52 -GoStart dt=10 g=47 g_seq=1 -UserRegionBegin dt=69 task=17 name_string=31 stack=26 -UserLog dt=11 task=17 key_string=24 value_string=86 stack=27 -UserLog dt=7 task=17 key_string=24 value_string=87 stack=30 -UserTaskBegin dt=5 task=18 parent_task=17 name_string=26 stack=31 -UserLog dt=7 task=18 key_string=27 value_string=88 stack=32 -UserRegionBegin dt=5 task=18 name_string=29 stack=33 -UserRegionEnd dt=4 task=18 name_string=29 stack=34 -HeapAlloc dt=35 heapalloc_value=1818624 -GoBlock dt=14 reason_string=19 stack=35 -HeapAlloc dt=11 heapalloc_value=1826816 -GoStart dt=10 g=45 g_seq=1 -UserRegionBegin dt=29 task=17 name_string=33 stack=26 -UserLog dt=9 task=17 key_string=24 value_string=89 stack=27 -UserRegionEnd dt=3 task=17 name_string=33 stack=28 -GoDestroy dt=1 -GoStart dt=5 g=46 g_seq=1 -UserRegionBegin dt=15 task=17 name_string=35 stack=26 -UserLog dt=8 task=17 key_string=24 value_string=90 stack=27 -UserRegionEnd dt=2 task=17 name_string=35 stack=28 -GoDestroy dt=1 -ProcStop dt=3 -ProcStart dt=141981 p=0 p_seq=16 -ProcStop dt=19 -ProcStart dt=17153 p=0 p_seq=17 -GoUnblock dt=44 g=1 g_seq=15 stack=0 -GoUnblock dt=11 g=47 g_seq=2 stack=0 -GoStart dt=215 g=47 g_seq=3 -UserTaskEnd dt=22 task=18 stack=36 -UserRegionEnd dt=9 task=17 name_string=31 stack=28 -GoDestroy dt=3 -GoStart dt=19 g=1 g_seq=16 -UserTaskEnd dt=13 task=17 stack=54 -UserLog dt=18 task=3 key_string=24 value_string=91 stack=55 -UserTaskBegin dt=7 task=19 parent_task=3 name_string=26 stack=56 -UserLog dt=27 task=19 key_string=27 value_string=92 stack=57 -UserRegionBegin dt=8 task=19 name_string=29 stack=58 -GoCreate dt=30 new_g=10 new_stack=17 stack=59 -GoCreate dt=9 new_g=11 new_stack=17 stack=59 -GoCreate dt=11 new_g=12 new_stack=17 stack=59 -GoCreate dt=7 new_g=13 new_stack=17 stack=59 -GoCreate dt=7 new_g=14 new_stack=17 stack=59 -GoCreate dt=9 new_g=15 new_stack=17 stack=59 -UserRegionEnd dt=5 task=19 name_string=29 stack=60 -GoBlock dt=7 reason_string=19 stack=61 -GoStart dt=17 g=15 g_seq=1 -UserRegionBegin dt=61 task=19 name_string=53 stack=26 -UserLog dt=10 task=19 key_string=24 value_string=93 stack=27 -UserRegionEnd dt=3 task=19 name_string=53 stack=28 -GoDestroy dt=1 -GoStart dt=4 g=10 g_seq=1 -UserRegionBegin dt=26 task=19 name_string=33 stack=26 -UserLog dt=7 task=19 key_string=24 value_string=94 stack=27 -UserRegionEnd dt=2 task=19 name_string=33 stack=28 -GoDestroy dt=1 -GoStart dt=4 g=11 g_seq=1 -UserRegionBegin dt=20 task=19 name_string=35 stack=26 -UserLog dt=5 task=19 key_string=24 value_string=95 stack=27 -UserRegionEnd dt=2 task=19 name_string=35 stack=28 -GoDestroy dt=1 -GoStart dt=7 g=12 g_seq=1 -UserRegionBegin dt=14 task=19 name_string=31 stack=26 -UserLog dt=4 task=19 key_string=24 value_string=96 stack=27 -UserLog dt=4 task=19 key_string=24 value_string=97 stack=30 -UserTaskBegin dt=7 task=20 parent_task=19 name_string=26 stack=31 -UserLog dt=5 task=20 key_string=27 value_string=98 stack=32 -UserRegionBegin dt=4 task=20 name_string=29 stack=33 -UserRegionEnd dt=5 task=20 name_string=29 stack=34 -GoBlock dt=9 reason_string=19 stack=35 -GoStart dt=9 g=14 g_seq=1 -UserRegionBegin dt=28 task=19 name_string=56 stack=26 -UserLog dt=7 task=19 key_string=24 value_string=99 stack=27 -UserRegionEnd dt=2 task=19 name_string=56 stack=28 -GoDestroy dt=2 -ProcStop dt=17 -ProcStart dt=141933 p=2 p_seq=11 -ProcStop dt=13 -ProcStart dt=16744 p=2 p_seq=12 -GoUnblock dt=29 g=1 g_seq=17 stack=0 -GoUnblock dt=7 g=12 g_seq=2 stack=0 -GoStart dt=172 g=12 g_seq=3 -UserTaskEnd dt=15 task=20 stack=36 -UserRegionEnd dt=8 task=19 name_string=31 stack=28 -GoDestroy dt=2 -GoStart dt=11 g=1 g_seq=18 -UserTaskEnd dt=14 task=19 stack=62 -UserLog dt=16 task=4 key_string=24 value_string=101 stack=63 -UserTaskBegin dt=6 task=21 parent_task=4 name_string=26 stack=64 -UserLog dt=25 task=21 key_string=27 value_string=102 stack=65 -UserRegionBegin dt=7 task=21 name_string=29 stack=66 -GoCreate dt=23 new_g=54 new_stack=17 stack=67 -GoCreate dt=8 new_g=55 new_stack=17 stack=67 -GoCreate dt=17 new_g=56 new_stack=17 stack=67 -GoCreate dt=8 new_g=57 new_stack=17 stack=67 -GoCreate dt=7 new_g=58 new_stack=17 stack=67 -UserRegionEnd dt=4 task=21 name_string=29 stack=68 -GoBlock dt=9 reason_string=19 stack=69 -GoStart dt=7 g=58 g_seq=1 -UserRegionBegin dt=46 task=21 name_string=56 stack=26 -UserLog dt=8 task=21 key_string=24 value_string=103 stack=27 -UserRegionEnd dt=4 task=21 name_string=56 stack=28 -GoDestroy dt=1 -GoStart dt=3 g=54 g_seq=1 -UserRegionBegin dt=19 task=21 name_string=33 stack=26 -UserLog dt=7 task=21 key_string=24 value_string=104 stack=27 -UserRegionEnd dt=2 task=21 name_string=33 stack=28 -GoDestroy dt=1 -GoStart dt=2 g=55 g_seq=1 -UserRegionBegin dt=17 task=21 name_string=35 stack=26 -UserLog dt=4 task=21 key_string=24 value_string=105 stack=27 -UserRegionEnd dt=2 task=21 name_string=35 stack=28 -GoDestroy dt=1 -GoStart dt=5 g=56 g_seq=1 -UserRegionBegin dt=16 task=21 name_string=31 stack=26 -UserLog dt=4 task=21 key_string=24 value_string=106 stack=27 -UserLog dt=3 task=21 key_string=24 value_string=107 stack=30 -UserTaskBegin dt=4 task=22 parent_task=21 name_string=26 stack=31 -UserLog dt=6 task=22 key_string=27 value_string=108 stack=32 -UserRegionBegin dt=4 task=22 name_string=29 stack=33 -UserRegionEnd dt=7 task=22 name_string=29 stack=34 -GoBlock dt=14 reason_string=19 stack=35 -GoStart dt=3 g=57 g_seq=1 -UserRegionBegin dt=22 task=21 name_string=30 stack=26 -UserLog dt=6 task=21 key_string=24 value_string=109 stack=27 -UserRegionEnd dt=2 task=21 name_string=30 stack=28 -GoDestroy dt=2 -ProcStop dt=10 -ProcStart dt=128031 p=4 p_seq=15 -ProcStop dt=16 -ProcStart dt=33758 p=2 p_seq=15 -ProcStop dt=18 -EventBatch gen=1 m=1986496 time=2753925246280 size=267 -ProcStart dt=549 p=0 p_seq=1 -GoStart dt=211 g=18 g_seq=1 -GoBlock dt=3533 reason_string=12 stack=21 -GoStart dt=41 g=21 g_seq=1 -GoBlock dt=150 reason_string=10 stack=22 -GoStart dt=93 g=20 g_seq=1 -GoSyscallBegin dt=51 p_seq=2 stack=23 -GoSyscallEnd dt=400 -GoBlock dt=582 reason_string=15 stack=25 -GoStart dt=26 g=23 g_seq=1 -HeapAlloc dt=50 heapalloc_value=1646592 -UserRegionBegin dt=2921 task=5 name_string=31 stack=26 -UserLog dt=28 task=5 key_string=24 value_string=37 stack=27 -UserLog dt=13 task=5 key_string=24 value_string=38 stack=30 -UserTaskBegin dt=15 task=6 parent_task=5 name_string=26 stack=31 -HeapAlloc dt=26 heapalloc_value=1687552 -UserLog dt=14 task=6 key_string=27 value_string=39 stack=32 -UserRegionBegin dt=9 task=6 name_string=29 stack=33 -UserRegionEnd dt=6 task=6 name_string=29 stack=34 -GoBlock dt=15 reason_string=19 stack=35 -ProcStop dt=30 -ProcStart dt=156949 p=4 p_seq=2 -GoUnblock dt=46 g=1 g_seq=1 stack=0 -GoStart dt=253 g=1 g_seq=2 -UserTaskEnd dt=27 task=5 stack=37 -UserLog dt=23 task=1 key_string=24 value_string=40 stack=38 -UserTaskBegin dt=14 task=7 parent_task=1 name_string=26 stack=39 -HeapAlloc dt=596 heapalloc_value=1695744 -HeapAlloc dt=18 heapalloc_value=1703936 -UserLog dt=17 task=7 key_string=27 value_string=41 stack=40 -UserRegionBegin dt=14 task=7 name_string=29 stack=41 -HeapAlloc dt=10 heapalloc_value=1712128 -HeapAlloc dt=17 heapalloc_value=1720320 -GoCreate dt=44 new_g=33 new_stack=17 stack=42 -GoCreate dt=175 new_g=34 new_stack=17 stack=42 -UserRegionEnd dt=50 task=7 name_string=29 stack=43 -GoBlock dt=9 reason_string=19 stack=44 -HeapAlloc dt=16 heapalloc_value=1728512 -GoStart dt=239 g=34 g_seq=1 -HeapAlloc dt=21 heapalloc_value=1736704 -UserRegionBegin dt=92 task=7 name_string=35 stack=26 -UserLog dt=15 task=7 key_string=24 value_string=42 stack=27 -UserRegionEnd dt=4 task=7 name_string=35 stack=28 -GoDestroy dt=2 -ProcStop dt=21 -ProcStart dt=800974 p=4 p_seq=10 -ProcStop dt=39 -ProcStart dt=158775 p=0 p_seq=15 -ProcStop dt=24 -ProcStart dt=159722 p=4 p_seq=13 -GoStart dt=254 g=13 g_seq=1 -UserRegionBegin dt=239 task=19 name_string=30 stack=26 -UserLog dt=23 task=19 key_string=24 value_string=100 stack=27 -UserRegionEnd dt=6 task=19 name_string=30 stack=28 -GoDestroy dt=7 -ProcStop dt=22 -EventBatch gen=1 m=1986495 time=2753925251756 size=320 -ProcStart dt=705 p=4 p_seq=1 -ProcStop dt=1279 -ProcStart dt=158975 p=0 p_seq=5 -ProcStop dt=23 -ProcStart dt=792 p=0 p_seq=6 -GoStart dt=187 g=33 g_seq=1 -UserRegionBegin dt=244 task=7 name_string=33 stack=26 -UserLog dt=32 task=7 key_string=24 value_string=43 stack=27 -UserRegionEnd dt=7 task=7 name_string=33 stack=28 -GoDestroy dt=5 -ProcStop dt=24 -ProcStart dt=160255 p=4 p_seq=4 -ProcStop dt=27 -ProcStart dt=159067 p=2 p_seq=5 -GoStart dt=222 g=37 g_seq=1 -UserRegionBegin dt=114 task=10 name_string=31 stack=26 -UserLog dt=16 task=10 key_string=24 value_string=60 stack=27 -UserLog dt=8 task=10 key_string=24 value_string=61 stack=30 -UserTaskBegin dt=8 task=11 parent_task=10 name_string=26 stack=31 -UserLog dt=19 task=11 key_string=27 value_string=62 stack=32 -UserRegionBegin dt=6 task=11 name_string=29 stack=33 -UserRegionEnd dt=7 task=11 name_string=29 stack=34 -GoBlock dt=15 reason_string=19 stack=35 -ProcStop dt=11 -ProcStart dt=160101 p=4 p_seq=7 -ProcStop dt=21 -ProcStart dt=159647 p=2 p_seq=7 -GoStart dt=277 g=43 g_seq=1 -UserRegionBegin dt=126 task=14 name_string=31 stack=26 -UserLog dt=21 task=14 key_string=24 value_string=77 stack=27 -UserLog dt=9 task=14 key_string=24 value_string=78 stack=30 -UserTaskBegin dt=8 task=15 parent_task=14 name_string=26 stack=31 -UserLog dt=17 task=15 key_string=27 value_string=79 stack=32 -UserRegionBegin dt=6 task=15 name_string=29 stack=33 -UserRegionEnd dt=8 task=15 name_string=29 stack=34 -GoBlock dt=23 reason_string=19 stack=35 -ProcStop dt=17 -ProcStart dt=159706 p=0 p_seq=14 -GoStart dt=229 g=52 g_seq=1 -UserRegionBegin dt=103 task=16 name_string=33 stack=26 -UserLog dt=20 task=16 key_string=24 value_string=83 stack=27 -UserRegionEnd dt=4 task=16 name_string=33 stack=28 -GoDestroy dt=3 -ProcStop dt=17 -ProcStart dt=319699 p=2 p_seq=10 -ProcStop dt=20 -ProcStart dt=158728 p=4 p_seq=14 -ProcStop dt=17 -ProcStart dt=110606 p=2 p_seq=13 -ProcStop dt=10 -ProcStart dt=16732 p=2 p_seq=14 -GoUnblock dt=45 g=18 g_seq=2 stack=0 -GoStart dt=184 g=18 g_seq=3 -GoBlock dt=114 reason_string=12 stack=21 -ProcStop dt=8 -ProcStart dt=16779 p=4 p_seq=16 -ProcStop dt=11 -ProcStart dt=16790 p=4 p_seq=17 -GoUnblock dt=23 g=1 g_seq=19 stack=0 -GoUnblock dt=8 g=56 g_seq=2 stack=0 -GoStart dt=142 g=56 g_seq=3 -UserTaskEnd dt=14 task=22 stack=36 -UserRegionEnd dt=8 task=21 name_string=31 stack=28 -GoDestroy dt=5 -GoStart dt=18 g=1 g_seq=20 -UserTaskEnd dt=17 task=21 stack=70 -UserTaskEnd dt=12 task=4 stack=71 -HeapAlloc dt=802 heapalloc_value=1835008 -HeapAlloc dt=41 heapalloc_value=1843200 -HeapAlloc dt=13 heapalloc_value=1851392 -EventBatch gen=1 m=1986494 time=2753925248778 size=47 -ProcStart dt=390 p=3 p_seq=1 -GoStart dt=1718 g=22 g_seq=1 -HeapAlloc dt=1807 heapalloc_value=1654784 -HeapAlloc dt=406 heapalloc_value=1671168 -HeapAlloc dt=15 heapalloc_value=1679360 -UserRegionBegin dt=49 task=5 name_string=35 stack=26 -UserLog dt=30 task=5 key_string=24 value_string=36 stack=27 -UserRegionEnd dt=5 task=5 name_string=35 stack=28 -GoDestroy dt=5 -ProcStop dt=42 -EventBatch gen=1 m=1986492 time=2753925244400 size=582 -ProcStatus dt=67 p=1 pstatus=1 -GoStatus dt=4 g=1 m=1986492 gstatus=2 -ProcsChange dt=220 procs_value=8 stack=1 -STWBegin dt=127 kind_string=21 stack=2 -HeapGoal dt=3 heapgoal_value=4194304 -ProcStatus dt=2 p=0 pstatus=2 -ProcStatus dt=2 p=2 pstatus=2 -ProcStatus dt=1 p=3 pstatus=2 -ProcStatus dt=1 p=4 pstatus=2 -ProcStatus dt=1 p=5 pstatus=2 -ProcStatus dt=1 p=6 pstatus=2 -ProcStatus dt=1 p=7 pstatus=2 -ProcsChange dt=353 procs_value=8 stack=3 -STWEnd dt=277 -HeapAlloc dt=243 heapalloc_value=1605632 -HeapAlloc dt=24 heapalloc_value=1613824 -GoCreate dt=209 new_g=18 new_stack=4 stack=5 -GoCreate dt=561 new_g=19 new_stack=6 stack=7 -GoCreate dt=25 new_g=20 new_stack=8 stack=9 -UserTaskEnd dt=309 task=2 stack=10 -UserTaskBegin dt=26 task=3 parent_task=1 name_string=22 stack=11 -UserTaskBegin dt=918 task=4 parent_task=0 name_string=23 stack=12 -UserLog dt=461 task=0 key_string=24 value_string=25 stack=13 -UserTaskBegin dt=420 task=5 parent_task=0 name_string=26 stack=14 -UserLog dt=673 task=5 key_string=27 value_string=28 stack=15 -UserRegionBegin dt=15 task=5 name_string=29 stack=16 -HeapAlloc dt=51 heapalloc_value=1630208 -GoCreate dt=24 new_g=21 new_stack=17 stack=18 -GoCreate dt=17 new_g=22 new_stack=17 stack=18 -GoCreate dt=10 new_g=23 new_stack=17 stack=18 -GoCreate dt=9 new_g=24 new_stack=17 stack=18 -UserRegionEnd dt=549 task=5 name_string=29 stack=19 -GoBlock dt=14 reason_string=19 stack=20 -GoStart dt=378 g=24 g_seq=1 -HeapAlloc dt=65 heapalloc_value=1638400 -GoUnblock dt=559 g=21 g_seq=2 stack=24 -UserRegionBegin dt=1498 task=5 name_string=30 stack=26 -UserLog dt=35 task=5 key_string=24 value_string=32 stack=27 -UserRegionEnd dt=8 task=5 name_string=30 stack=28 -GoDestroy dt=5 -GoStart dt=24 g=21 g_seq=3 -UserRegionBegin dt=60 task=5 name_string=33 stack=26 -UserLog dt=7 task=5 key_string=24 value_string=34 stack=27 -UserRegionEnd dt=2 task=5 name_string=33 stack=28 -GoDestroy dt=2 -ProcStop dt=34 -ProcStart dt=141874 p=0 p_seq=3 -ProcStop dt=21 -ProcStart dt=16770 p=0 p_seq=4 -GoUnblock dt=29 g=23 g_seq=2 stack=0 -GoStart dt=176 g=23 g_seq=3 -UserTaskEnd dt=19 task=6 stack=36 -UserRegionEnd dt=14 task=5 name_string=31 stack=28 -GoDestroy dt=2 -ProcStop dt=12 -ProcStart dt=2251 p=4 p_seq=3 -ProcStop dt=22 -ProcStart dt=141952 p=2 p_seq=3 -ProcStop dt=27 -ProcStart dt=16789 p=2 p_seq=4 -GoUnblock dt=35 g=1 g_seq=3 stack=0 -GoStart dt=214 g=1 g_seq=4 -UserTaskEnd dt=26 task=7 stack=45 -UserLog dt=27 task=2 key_string=24 value_string=44 stack=46 -UserTaskBegin dt=10 task=8 parent_task=2 name_string=26 stack=47 -HeapAlloc dt=52 heapalloc_value=1744896 -HeapAlloc dt=22 heapalloc_value=1753088 -UserLog dt=13 task=8 key_string=27 value_string=45 stack=48 -UserRegionBegin dt=11 task=8 name_string=29 stack=49 -HeapAlloc dt=7 heapalloc_value=1761280 -HeapAlloc dt=18 heapalloc_value=1769472 -GoCreate dt=52 new_g=49 new_stack=17 stack=50 -GoCreate dt=12 new_g=50 new_stack=17 stack=50 -HeapAlloc dt=11 heapalloc_value=1777664 -GoCreate dt=9 new_g=51 new_stack=17 stack=50 -UserRegionEnd dt=9 task=8 name_string=29 stack=51 -GoBlock dt=11 reason_string=19 stack=52 -HeapAlloc dt=12 heapalloc_value=1785856 -GoStart dt=14 g=51 g_seq=1 -HeapAlloc dt=18 heapalloc_value=1794048 -UserRegionBegin dt=95 task=8 name_string=31 stack=26 -UserLog dt=22 task=8 key_string=24 value_string=46 stack=27 -UserLog dt=8 task=8 key_string=24 value_string=47 stack=30 -UserTaskBegin dt=5 task=9 parent_task=8 name_string=26 stack=31 -UserLog dt=7 task=9 key_string=27 value_string=48 stack=32 -UserRegionBegin dt=4 task=9 name_string=29 stack=33 -UserRegionEnd dt=7 task=9 name_string=29 stack=34 -HeapAlloc dt=11 heapalloc_value=1802240 -GoStop dt=674 reason_string=16 stack=53 -GoStart dt=12 g=51 g_seq=2 -GoBlock dt=8 reason_string=19 stack=35 -HeapAlloc dt=16 heapalloc_value=1810432 -ProcStop dt=8 -ProcStart dt=159907 p=0 p_seq=8 -ProcStop dt=25 -ProcStart dt=159186 p=2 p_seq=6 -GoUnblock dt=22 g=37 g_seq=2 stack=0 -GoStart dt=217 g=37 g_seq=3 -UserTaskEnd dt=19 task=11 stack=36 -UserRegionEnd dt=15 task=10 name_string=31 stack=28 -GoDestroy dt=5 -ProcStop dt=16 -ProcStart dt=160988 p=0 p_seq=11 -ProcStop dt=29 -ProcStart dt=158554 p=2 p_seq=8 -GoUnblock dt=38 g=1 g_seq=11 stack=0 -GoStart dt=240 g=1 g_seq=12 -UserTaskEnd dt=25 task=14 stack=37 -UserLog dt=23 task=1 key_string=24 value_string=80 stack=38 -UserTaskBegin dt=11 task=16 parent_task=1 name_string=26 stack=39 -UserLog dt=36 task=16 key_string=27 value_string=81 stack=40 -UserRegionBegin dt=13 task=16 name_string=29 stack=41 -GoCreate dt=39 new_g=52 new_stack=17 stack=42 -GoCreate dt=23 new_g=53 new_stack=17 stack=42 -UserRegionEnd dt=11 task=16 name_string=29 stack=43 -GoBlock dt=9 reason_string=19 stack=44 -GoStart dt=244 g=53 g_seq=1 -UserRegionBegin dt=101 task=16 name_string=35 stack=26 -UserLog dt=17 task=16 key_string=24 value_string=82 stack=27 -UserRegionEnd dt=4 task=16 name_string=35 stack=28 -GoDestroy dt=3 -ProcStop dt=28 -EventBatch gen=1 m=18446744073709551615 time=2753926855140 size=56 -GoStatus dt=74 g=2 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=3 m=18446744073709551615 gstatus=4 -GoStatus dt=1 g=4 m=18446744073709551615 gstatus=4 -GoStatus dt=1 g=17 m=18446744073709551615 gstatus=4 -EventBatch gen=1 m=18446744073709551615 time=2753926855560 size=1759 -Stacks -Stack id=45 nframes=3 - pc=4804964 func=110 file=111 line=80 - pc=4804052 func=112 file=113 line=84 - pc=4803566 func=114 file=113 line=44 -Stack id=22 nframes=7 - pc=4633935 func=115 file=116 line=90 - pc=4633896 func=117 file=118 line=223 - pc=4633765 func=119 file=118 line=216 - pc=4633083 func=120 file=118 line=131 - pc=4764601 func=121 file=122 line=152 - pc=4765335 func=123 file=122 line=238 - pc=4804612 func=124 file=113 line=70 -Stack id=9 nframes=2 - pc=4802543 func=125 file=126 line=128 - pc=4803332 func=114 file=113 line=30 -Stack id=71 nframes=2 - pc=4803671 func=110 file=111 line=80 - pc=4803666 func=114 file=113 line=51 -Stack id=10 nframes=2 - pc=4803415 func=110 file=111 line=80 - pc=4803410 func=114 file=113 line=33 -Stack id=18 nframes=4 - pc=4804196 func=127 file=113 line=69 - pc=4802140 func=128 file=111 line=141 - pc=4804022 func=112 file=113 line=67 - pc=4803543 func=114 file=113 line=43 -Stack id=37 nframes=3 - pc=4804964 func=110 file=111 line=80 - pc=4804052 func=112 file=113 line=84 - pc=4803543 func=114 file=113 line=43 -Stack id=31 nframes=4 - pc=4803865 func=112 file=113 line=61 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=55 nframes=2 - pc=4803832 func=112 file=113 line=58 - pc=4803609 func=114 file=113 line=46 -Stack id=47 nframes=2 - pc=4803865 func=112 file=113 line=61 - pc=4803589 func=114 file=113 line=45 -Stack id=38 nframes=2 - pc=4803832 func=112 file=113 line=58 - pc=4803566 func=114 file=113 line=44 -Stack id=56 nframes=2 - pc=4803865 func=112 file=113 line=61 - pc=4803609 func=114 file=113 line=46 -Stack id=33 nframes=4 - pc=4804022 func=112 file=113 line=67 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=44 nframes=3 - pc=4599892 func=130 file=131 line=195 - pc=4804036 func=112 file=113 line=83 - pc=4803566 func=114 file=113 line=44 -Stack id=3 nframes=4 - pc=4421707 func=132 file=133 line=1382 - pc=4533555 func=134 file=135 line=255 - pc=4802469 func=125 file=126 line=125 - pc=4803332 func=114 file=113 line=30 -Stack id=6 nframes=1 - pc=4539520 func=136 file=135 line=868 -Stack id=58 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803609 func=114 file=113 line=46 -Stack id=64 nframes=2 - pc=4803865 func=112 file=113 line=61 - pc=4803629 func=114 file=113 line=47 -Stack id=62 nframes=3 - pc=4804964 func=110 file=111 line=80 - pc=4804052 func=112 file=113 line=84 - pc=4803609 func=114 file=113 line=46 -Stack id=34 nframes=4 - pc=4804022 func=112 file=113 line=67 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=30 nframes=4 - pc=4803832 func=112 file=113 line=58 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=32 nframes=4 - pc=4803943 func=112 file=113 line=64 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=26 nframes=1 - pc=4804691 func=124 file=113 line=70 -Stack id=46 nframes=2 - pc=4803832 func=112 file=113 line=58 - pc=4803589 func=114 file=113 line=45 -Stack id=50 nframes=4 - pc=4804196 func=127 file=113 line=69 - pc=4802140 func=128 file=111 line=141 - pc=4804022 func=112 file=113 line=67 - pc=4803589 func=114 file=113 line=45 -Stack id=59 nframes=4 - pc=4804196 func=127 file=113 line=69 - pc=4802140 func=128 file=111 line=141 - pc=4804022 func=112 file=113 line=67 - pc=4803609 func=114 file=113 line=46 -Stack id=7 nframes=4 - pc=4539492 func=137 file=135 line=868 - pc=4533572 func=134 file=135 line=258 - pc=4802469 func=125 file=126 line=125 - pc=4803332 func=114 file=113 line=30 -Stack id=17 nframes=1 - pc=4804512 func=124 file=113 line=69 -Stack id=57 nframes=2 - pc=4803943 func=112 file=113 line=64 - pc=4803609 func=114 file=113 line=46 -Stack id=41 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803566 func=114 file=113 line=44 -Stack id=63 nframes=2 - pc=4803832 func=112 file=113 line=58 - pc=4803629 func=114 file=113 line=47 -Stack id=60 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803609 func=114 file=113 line=46 -Stack id=5 nframes=4 - pc=4542549 func=138 file=139 line=42 - pc=4533560 func=134 file=135 line=257 - pc=4802469 func=125 file=126 line=125 - pc=4803332 func=114 file=113 line=30 -Stack id=40 nframes=2 - pc=4803943 func=112 file=113 line=64 - pc=4803566 func=114 file=113 line=44 -Stack id=21 nframes=3 - pc=4217905 func=140 file=141 line=442 - pc=4539946 func=142 file=135 line=928 - pc=4542714 func=143 file=139 line=54 -Stack id=2 nframes=3 - pc=4533284 func=134 file=135 line=238 - pc=4802469 func=125 file=126 line=125 - pc=4803332 func=114 file=113 line=30 -Stack id=53 nframes=6 - pc=4247492 func=144 file=145 line=1374 - pc=4599676 func=130 file=131 line=186 - pc=4804036 func=112 file=113 line=83 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=20 nframes=3 - pc=4599892 func=130 file=131 line=195 - pc=4804036 func=112 file=113 line=83 - pc=4803543 func=114 file=113 line=43 -Stack id=70 nframes=3 - pc=4804964 func=110 file=111 line=80 - pc=4804052 func=112 file=113 line=84 - pc=4803629 func=114 file=113 line=47 -Stack id=15 nframes=2 - pc=4803943 func=112 file=113 line=64 - pc=4803543 func=114 file=113 line=43 -Stack id=65 nframes=2 - pc=4803943 func=112 file=113 line=64 - pc=4803629 func=114 file=113 line=47 -Stack id=28 nframes=1 - pc=4804691 func=124 file=113 line=70 -Stack id=48 nframes=2 - pc=4803943 func=112 file=113 line=64 - pc=4803589 func=114 file=113 line=45 -Stack id=61 nframes=3 - pc=4599892 func=130 file=131 line=195 - pc=4804036 func=112 file=113 line=83 - pc=4803609 func=114 file=113 line=46 -Stack id=13 nframes=2 - pc=4803832 func=112 file=113 line=58 - pc=4803543 func=114 file=113 line=43 -Stack id=29 nframes=3 - pc=4217905 func=140 file=141 line=442 - pc=4539946 func=142 file=135 line=928 - pc=4539559 func=136 file=135 line=871 -Stack id=51 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803589 func=114 file=113 line=45 -Stack id=42 nframes=4 - pc=4804196 func=127 file=113 line=69 - pc=4802140 func=128 file=111 line=141 - pc=4804022 func=112 file=113 line=67 - pc=4803566 func=114 file=113 line=44 -Stack id=14 nframes=2 - pc=4803865 func=112 file=113 line=61 - pc=4803543 func=114 file=113 line=43 -Stack id=39 nframes=2 - pc=4803865 func=112 file=113 line=61 - pc=4803566 func=114 file=113 line=44 -Stack id=49 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803589 func=114 file=113 line=45 -Stack id=52 nframes=3 - pc=4599892 func=130 file=131 line=195 - pc=4804036 func=112 file=113 line=83 - pc=4803589 func=114 file=113 line=45 -Stack id=24 nframes=7 - pc=4634510 func=146 file=116 line=223 - pc=4634311 func=117 file=118 line=240 - pc=4633765 func=119 file=118 line=216 - pc=4633083 func=120 file=118 line=131 - pc=4764601 func=121 file=122 line=152 - pc=4765335 func=123 file=122 line=238 - pc=4804612 func=124 file=113 line=70 -Stack id=43 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803566 func=114 file=113 line=44 -Stack id=19 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803543 func=114 file=113 line=43 -Stack id=69 nframes=3 - pc=4599892 func=130 file=131 line=195 - pc=4804036 func=112 file=113 line=83 - pc=4803629 func=114 file=113 line=47 -Stack id=16 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803543 func=114 file=113 line=43 -Stack id=54 nframes=3 - pc=4804964 func=110 file=111 line=80 - pc=4804052 func=112 file=113 line=84 - pc=4803589 func=114 file=113 line=45 -Stack id=35 nframes=5 - pc=4599892 func=130 file=131 line=195 - pc=4804036 func=112 file=113 line=83 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=27 nframes=3 - pc=4804862 func=129 file=113 line=71 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=4 nframes=1 - pc=4542656 func=143 file=139 line=42 -Stack id=8 nframes=1 - pc=4802720 func=147 file=126 line=128 -Stack id=66 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803629 func=114 file=113 line=47 -Stack id=1 nframes=4 - pc=4548715 func=148 file=149 line=255 - pc=4533263 func=134 file=135 line=237 - pc=4802469 func=125 file=126 line=125 - pc=4803332 func=114 file=113 line=30 -Stack id=67 nframes=4 - pc=4804196 func=127 file=113 line=69 - pc=4802140 func=128 file=111 line=141 - pc=4804022 func=112 file=113 line=67 - pc=4803629 func=114 file=113 line=47 -Stack id=23 nframes=7 - pc=4641050 func=150 file=151 line=964 - pc=4751591 func=152 file=153 line=209 - pc=4751583 func=154 file=155 line=736 - pc=4751136 func=156 file=155 line=380 - pc=4753008 func=157 file=158 line=46 - pc=4753000 func=159 file=160 line=183 - pc=4802778 func=147 file=126 line=134 -Stack id=11 nframes=1 - pc=4803445 func=114 file=113 line=36 -Stack id=68 nframes=2 - pc=4804022 func=112 file=113 line=67 - pc=4803629 func=114 file=113 line=47 -Stack id=36 nframes=5 - pc=4804964 func=110 file=111 line=80 - pc=4804052 func=112 file=113 line=84 - pc=4804890 func=129 file=113 line=73 - pc=4802140 func=128 file=111 line=141 - pc=4804691 func=124 file=113 line=70 -Stack id=12 nframes=1 - pc=4803492 func=114 file=113 line=39 -Stack id=25 nframes=1 - pc=4802788 func=147 file=126 line=130 -EventBatch gen=1 m=18446744073709551615 time=2753925243266 size=3466 -Strings -String id=1 - data="Not worker" -String id=2 - data="GC (dedicated)" -String id=3 - data="GC (fractional)" -String id=4 - data="GC (idle)" -String id=5 - data="unspecified" -String id=6 - data="forever" -String id=7 - data="network" -String id=8 - data="select" -String id=9 - data="sync.(*Cond).Wait" -String id=10 - data="sync" -String id=11 - data="chan send" -String id=12 - data="chan receive" -String id=13 - data="GC mark assist wait for work" -String id=14 - data="GC background sweeper wait" -String id=15 - data="system goroutine wait" -String id=16 - data="preempted" -String id=17 - data="wait for debug call" -String id=18 - data="wait until GC ends" -String id=19 - data="sleep" -String id=20 - data="runtime.Gosched" -String id=21 - data="start trace" -String id=22 - data="type2" -String id=23 - data="type3" -String id=24 - data="log" -String id=25 - data="before do" -String id=26 - data="do" -String id=27 - data="log2" -String id=28 - data="do" -String id=29 - data="fanout" -String id=30 - data="region3" -String id=31 - data="region2" -String id=32 - data="fanout region3" -String id=33 - data="region0" -String id=34 - data="fanout region0" -String id=35 - data="region1" -String id=36 - data="fanout region1" -String id=37 - data="fanout region2" -String id=38 - data="before do" -String id=39 - data="do" -String id=40 - data="before do" -String id=41 - data="do" -String id=42 - data="fanout region1" -String id=43 - data="fanout region0" -String id=44 - data="before do" -String id=45 - data="do" -String id=46 - data="fanout region2" -String id=47 - data="before do" -String id=48 - data="do" -String id=49 - data="fanout region0" -String id=50 - data="fanout region1" -String id=51 - data="before do" -String id=52 - data="do" -String id=53 - data="region5" -String id=54 - data="fanout region5" -String id=55 - data="fanout region3" -String id=56 - data="region4" -String id=57 - data="fanout region4" -String id=58 - data="fanout region0" -String id=59 - data="fanout region1" -String id=60 - data="fanout region2" -String id=61 - data="before do" -String id=62 - data="do" -String id=63 - data="before do" -String id=64 - data="do" -String id=65 - data="fanout region4" -String id=66 - data="fanout region0" -String id=67 - data="fanout region1" -String id=68 - data="fanout region2" -String id=69 - data="before do" -String id=70 - data="do" -String id=71 - data="fanout region3" -String id=72 - data="before do" -String id=73 - data="do" -String id=74 - data="fanout region3" -String id=75 - data="fanout region0" -String id=76 - data="fanout region1" -String id=77 - data="fanout region2" -String id=78 - data="before do" -String id=79 - data="do" -String id=80 - data="before do" -String id=81 - data="do" -String id=82 - data="fanout region1" -String id=83 - data="fanout region0" -String id=84 - data="before do" -String id=85 - data="do" -String id=86 - data="fanout region2" -String id=87 - data="before do" -String id=88 - data="do" -String id=89 - data="fanout region0" -String id=90 - data="fanout region1" -String id=91 - data="before do" -String id=92 - data="do" -String id=93 - data="fanout region5" -String id=94 - data="fanout region0" -String id=95 - data="fanout region1" -String id=96 - data="fanout region2" -String id=97 - data="before do" -String id=98 - data="do" -String id=99 - data="fanout region4" -String id=100 - data="fanout region3" -String id=101 - data="before do" -String id=102 - data="do" -String id=103 - data="fanout region4" -String id=104 - data="fanout region0" -String id=105 - data="fanout region1" -String id=106 - data="fanout region2" -String id=107 - data="before do" -String id=108 - data="do" -String id=109 - data="fanout region3" -String id=110 - data="runtime/trace.(*Task).End" -String id=111 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/annotation.go" -String id=112 - data="main.do" -String id=113 - data="/usr/local/google/home/mknyszek/work/go-1/src/internal/trace/v2/testdata/testprog/annotations-stress.go" -String id=114 - data="main.main" -String id=115 - data="sync.(*Mutex).Lock" -String id=116 - data="/usr/local/google/home/mknyszek/work/go-1/src/sync/mutex.go" -String id=117 - data="sync.(*Pool).pinSlow" -String id=118 - data="/usr/local/google/home/mknyszek/work/go-1/src/sync/pool.go" -String id=119 - data="sync.(*Pool).pin" -String id=120 - data="sync.(*Pool).Get" -String id=121 - data="fmt.newPrinter" -String id=122 - data="/usr/local/google/home/mknyszek/work/go-1/src/fmt/print.go" -String id=123 - data="fmt.Sprintf" -String id=124 - data="main.do.func1.1" -String id=125 - data="runtime/trace.Start" -String id=126 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" -String id=127 - data="main.do.func1" -String id=128 - data="runtime/trace.WithRegion" -String id=129 - data="main.do.func1.1.1" -String id=130 - data="time.Sleep" -String id=131 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/time.go" -String id=132 - data="runtime.startTheWorld" -String id=133 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" -String id=134 - data="runtime.StartTrace" -String id=135 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" -String id=136 - data="runtime.(*traceAdvancerState).start.func1" -String id=137 - data="runtime.(*traceAdvancerState).start" -String id=138 - data="runtime.traceStartReadCPU" -String id=139 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2cpu.go" -String id=140 - data="runtime.chanrecv1" -String id=141 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" -String id=142 - data="runtime.(*wakeableSleep).sleep" -String id=143 - data="runtime.traceStartReadCPU.func1" -String id=144 - data="runtime.newobject" -String id=145 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/malloc.go" -String id=146 - data="sync.(*Mutex).Unlock" -String id=147 - data="runtime/trace.Start.func1" -String id=148 - data="runtime.traceLocker.Gomaxprocs" -String id=149 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" -String id=150 - data="syscall.write" -String id=151 - data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/zsyscall_linux_amd64.go" -String id=152 - data="syscall.Write" -String id=153 - data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/syscall_unix.go" -String id=154 - data="internal/poll.ignoringEINTRIO" -String id=155 - data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_unix.go" -String id=156 - data="internal/poll.(*FD).Write" -String id=157 - data="os.(*File).write" -String id=158 - data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_posix.go" -String id=159 - data="os.(*File).Write" -String id=160 - data="/usr/local/google/home/mknyszek/work/go-1/src/os/file.go" diff --git a/src/internal/trace/v2/testdata/tests/go122-annotations.test b/src/internal/trace/v2/testdata/tests/go122-annotations.test deleted file mode 100644 index e468673497..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-annotations.test +++ /dev/null @@ -1,299 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=18446744073709551615 time=28113086279559 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=167930 time=28113086277797 size=41 -ProcStart dt=505 p=1 p_seq=1 -GoStart dt=303 g=7 g_seq=1 -HeapAlloc dt=646 heapalloc_value=1892352 -HeapAlloc dt=149 heapalloc_value=1900544 -GoBlock dt=146 reason_string=12 stack=24 -GoStart dt=14 g=6 g_seq=1 -HeapAlloc dt=16 heapalloc_value=1908736 -GoBlock dt=347 reason_string=12 stack=25 -EventBatch gen=1 m=167928 time=28113086279032 size=10 -ProcStart dt=451 p=2 p_seq=1 -GoStart dt=188 g=8 g_seq=1 -EventBatch gen=1 m=167926 time=28113086275999 size=324 -ProcStatus dt=295 p=0 pstatus=1 -GoStatus dt=5 g=1 m=167926 gstatus=2 -ProcsChange dt=405 procs_value=48 stack=1 -STWBegin dt=65 kind_string=21 stack=2 -HeapGoal dt=2 heapgoal_value=4194304 -ProcStatus dt=4 p=1 pstatus=2 -ProcStatus dt=1 p=2 pstatus=2 -ProcStatus dt=1 p=3 pstatus=2 -ProcStatus dt=1 p=4 pstatus=2 -ProcStatus dt=1 p=5 pstatus=2 -ProcStatus dt=1 p=6 pstatus=2 -ProcStatus dt=1 p=7 pstatus=2 -ProcStatus dt=1 p=8 pstatus=2 -ProcStatus dt=1 p=9 pstatus=2 -ProcStatus dt=1 p=10 pstatus=2 -ProcStatus dt=1 p=11 pstatus=2 -ProcStatus dt=1 p=12 pstatus=2 -ProcStatus dt=1 p=13 pstatus=2 -ProcStatus dt=1 p=14 pstatus=2 -ProcStatus dt=1 p=15 pstatus=2 -ProcStatus dt=1 p=16 pstatus=2 -ProcStatus dt=1 p=17 pstatus=2 -ProcStatus dt=1 p=18 pstatus=2 -ProcStatus dt=1 p=19 pstatus=2 -ProcStatus dt=1 p=20 pstatus=2 -ProcStatus dt=1 p=21 pstatus=2 -ProcStatus dt=1 p=22 pstatus=2 -ProcStatus dt=1 p=23 pstatus=2 -ProcStatus dt=1 p=24 pstatus=2 -ProcStatus dt=1 p=25 pstatus=2 -ProcStatus dt=1 p=26 pstatus=2 -ProcStatus dt=1 p=27 pstatus=2 -ProcStatus dt=1 p=28 pstatus=2 -ProcStatus dt=1 p=29 pstatus=2 -ProcStatus dt=1 p=30 pstatus=2 -ProcStatus dt=1 p=31 pstatus=2 -ProcStatus dt=1 p=32 pstatus=2 -ProcStatus dt=1 p=33 pstatus=2 -ProcStatus dt=1 p=34 pstatus=2 -ProcStatus dt=1 p=35 pstatus=2 -ProcStatus dt=1 p=36 pstatus=2 -ProcStatus dt=1 p=37 pstatus=2 -ProcStatus dt=1 p=38 pstatus=2 -ProcStatus dt=1 p=39 pstatus=2 -ProcStatus dt=1 p=40 pstatus=2 -ProcStatus dt=1 p=41 pstatus=2 -ProcStatus dt=1 p=42 pstatus=2 -ProcStatus dt=1 p=43 pstatus=2 -ProcStatus dt=1 p=44 pstatus=2 -ProcStatus dt=1 p=45 pstatus=2 -ProcStatus dt=1 p=46 pstatus=2 -ProcStatus dt=1 p=47 pstatus=2 -ProcsChange dt=1 procs_value=48 stack=3 -STWEnd dt=184 -GoCreate dt=252 new_g=6 new_stack=4 stack=5 -GoCreate dt=78 new_g=7 new_stack=6 stack=7 -GoCreate dt=73 new_g=8 new_stack=8 stack=9 -UserTaskBegin dt=71 task=1 parent_task=0 name_string=22 stack=10 -UserRegionBegin dt=535 task=1 name_string=23 stack=11 -HeapAlloc dt=26 heapalloc_value=1884160 -GoCreate dt=8 new_g=9 new_stack=12 stack=13 -GoBlock dt=249 reason_string=10 stack=14 -GoStart dt=8 g=9 g_seq=1 -UserRegionBegin dt=286 task=1 name_string=24 stack=15 -UserRegionBegin dt=244 task=1 name_string=25 stack=16 -UserRegionBegin dt=6 task=1 name_string=26 stack=17 -UserLog dt=6 task=1 key_string=27 value_string=28 stack=18 -UserRegionEnd dt=4 task=1 name_string=26 stack=19 -UserRegionEnd dt=315 task=1 name_string=25 stack=20 -UserTaskEnd dt=5 task=1 stack=21 -GoUnblock dt=11 g=1 g_seq=1 stack=22 -GoDestroy dt=6 -GoStart dt=10 g=1 g_seq=2 -UserRegionBegin dt=278 task=0 name_string=29 stack=23 -EventBatch gen=1 m=18446744073709551615 time=28113086280061 size=57 -GoStatus dt=318 g=2 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=3 m=18446744073709551615 gstatus=4 -GoStatus dt=1 g=4 m=18446744073709551615 gstatus=4 -GoStatus dt=1 g=5 m=18446744073709551615 gstatus=4 -EventBatch gen=1 m=18446744073709551615 time=28113086280852 size=488 -Stacks -Stack id=17 nframes=3 - pc=4816080 func=30 file=31 line=45 - pc=4813660 func=32 file=33 line=141 - pc=4815908 func=34 file=31 line=43 -Stack id=8 nframes=1 - pc=4814528 func=35 file=36 line=128 -Stack id=9 nframes=2 - pc=4814351 func=37 file=36 line=128 - pc=4815228 func=38 file=31 line=27 -Stack id=24 nframes=3 - pc=4217457 func=39 file=40 line=442 - pc=4544973 func=41 file=42 line=918 - pc=4544806 func=43 file=42 line=871 -Stack id=7 nframes=4 - pc=4544740 func=44 file=42 line=868 - pc=4538792 func=45 file=42 line=258 - pc=4814277 func=37 file=36 line=125 - pc=4815228 func=38 file=31 line=27 -Stack id=22 nframes=3 - pc=4642148 func=46 file=47 line=81 - pc=4816326 func=48 file=47 line=87 - pc=4815941 func=34 file=31 line=50 -Stack id=11 nframes=1 - pc=4815364 func=38 file=31 line=34 -Stack id=5 nframes=4 - pc=4547349 func=49 file=50 line=42 - pc=4538780 func=45 file=42 line=257 - pc=4814277 func=37 file=36 line=125 - pc=4815228 func=38 file=31 line=27 -Stack id=23 nframes=1 - pc=4815568 func=38 file=31 line=54 -Stack id=3 nframes=4 - pc=4421860 func=51 file=52 line=1360 - pc=4538775 func=45 file=42 line=255 - pc=4814277 func=37 file=36 line=125 - pc=4815228 func=38 file=31 line=27 -Stack id=21 nframes=2 - pc=4816228 func=53 file=33 line=80 - pc=4815926 func=34 file=31 line=50 -Stack id=1 nframes=4 - pc=4553515 func=54 file=55 line=255 - pc=4538503 func=45 file=42 line=237 - pc=4814277 func=37 file=36 line=125 - pc=4815228 func=38 file=31 line=27 -Stack id=12 nframes=1 - pc=4815680 func=34 file=31 line=37 -Stack id=6 nframes=1 - pc=4544768 func=43 file=42 line=868 -Stack id=2 nframes=3 - pc=4538523 func=45 file=42 line=238 - pc=4814277 func=37 file=36 line=125 - pc=4815228 func=38 file=31 line=27 -Stack id=13 nframes=1 - pc=4815492 func=38 file=31 line=37 -Stack id=4 nframes=1 - pc=4547456 func=56 file=50 line=42 -Stack id=14 nframes=2 - pc=4642407 func=57 file=47 line=116 - pc=4815502 func=38 file=31 line=51 -Stack id=18 nframes=5 - pc=4816147 func=58 file=31 line=46 - pc=4813660 func=32 file=33 line=141 - pc=4816080 func=30 file=31 line=45 - pc=4813660 func=32 file=33 line=141 - pc=4815908 func=34 file=31 line=43 -Stack id=20 nframes=1 - pc=4815908 func=34 file=31 line=43 -Stack id=25 nframes=3 - pc=4217457 func=39 file=40 line=442 - pc=4544973 func=41 file=42 line=918 - pc=4547514 func=56 file=50 line=54 -Stack id=16 nframes=1 - pc=4815908 func=34 file=31 line=43 -Stack id=15 nframes=1 - pc=4815838 func=34 file=31 line=41 -Stack id=19 nframes=3 - pc=4816080 func=30 file=31 line=45 - pc=4813660 func=32 file=33 line=141 - pc=4815908 func=34 file=31 line=43 -Stack id=10 nframes=1 - pc=4815332 func=38 file=31 line=33 -EventBatch gen=1 m=18446744073709551615 time=28113086274600 size=1620 -Strings -String id=1 - data="Not worker" -String id=2 - data="GC (dedicated)" -String id=3 - data="GC (fractional)" -String id=4 - data="GC (idle)" -String id=5 - data="unspecified" -String id=6 - data="forever" -String id=7 - data="network" -String id=8 - data="select" -String id=9 - data="sync.(*Cond).Wait" -String id=10 - data="sync" -String id=11 - data="chan send" -String id=12 - data="chan receive" -String id=13 - data="GC mark assist wait for work" -String id=14 - data="GC background sweeper wait" -String id=15 - data="system goroutine wait" -String id=16 - data="preempted" -String id=17 - data="wait for debug call" -String id=18 - data="wait until GC ends" -String id=19 - data="sleep" -String id=20 - data="runtime.Gosched" -String id=21 - data="start trace" -String id=22 - data="task0" -String id=23 - data="task0 region" -String id=24 - data="unended region" -String id=25 - data="region0" -String id=26 - data="region1" -String id=27 - data="key0" -String id=28 - data="0123456789abcdef" -String id=29 - data="post-existing region" -String id=30 - data="main.main.func1.1" -String id=31 - data="/usr/local/google/home/mknyszek/work/go-1/src/internal/trace/v2/testdata/testprog/annotations.go" -String id=32 - data="runtime/trace.WithRegion" -String id=33 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/annotation.go" -String id=34 - data="main.main.func1" -String id=35 - data="runtime/trace.Start.func1" -String id=36 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" -String id=37 - data="runtime/trace.Start" -String id=38 - data="main.main" -String id=39 - data="runtime.chanrecv1" -String id=40 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" -String id=41 - data="runtime.(*wakeableSleep).sleep" -String id=42 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" -String id=43 - data="runtime.(*traceAdvancerState).start.func1" -String id=44 - data="runtime.(*traceAdvancerState).start" -String id=45 - data="runtime.StartTrace" -String id=46 - data="sync.(*WaitGroup).Add" -String id=47 - data="/usr/local/google/home/mknyszek/work/go-1/src/sync/waitgroup.go" -String id=48 - data="sync.(*WaitGroup).Done" -String id=49 - data="runtime.traceStartReadCPU" -String id=50 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2cpu.go" -String id=51 - data="runtime.startTheWorld" -String id=52 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" -String id=53 - data="runtime/trace.(*Task).End" -String id=54 - data="runtime.traceLocker.Gomaxprocs" -String id=55 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" -String id=56 - data="runtime.traceStartReadCPU.func1" -String id=57 - data="sync.(*WaitGroup).Wait" -String id=58 - data="main.main.func1.1.1" diff --git a/src/internal/trace/v2/testdata/tests/go122-confuse-seq-across-generations.test b/src/internal/trace/v2/testdata/tests/go122-confuse-seq-across-generations.test deleted file mode 100644 index c0d6f0d3dd..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-confuse-seq-across-generations.test +++ /dev/null @@ -1,36 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=13 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=2 -GoStop dt=1 reason_string=1 stack=0 -EventBatch gen=1 m=1 time=0 size=12 -ProcStatus dt=1 p=1 pstatus=1 -GoStart dt=1 g=1 g_seq=1 -GoStop dt=1 reason_string=1 stack=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=12 -Strings -String id=1 - data="whatever" -EventBatch gen=2 m=1 time=3 size=8 -ProcStatus dt=1 p=1 pstatus=1 -GoStart dt=1 g=1 g_seq=2 -EventBatch gen=2 m=0 time=5 size=17 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=1 -GoStart dt=1 g=1 g_seq=1 -GoStop dt=1 reason_string=1 stack=0 -EventBatch gen=2 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=2 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=2 m=18446744073709551615 time=0 size=12 -Strings -String id=1 - data="whatever" diff --git a/src/internal/trace/v2/testdata/tests/go122-create-syscall-reuse-thread-id.test b/src/internal/trace/v2/testdata/tests/go122-create-syscall-reuse-thread-id.test deleted file mode 100644 index 1820738384..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-create-syscall-reuse-thread-id.test +++ /dev/null @@ -1,23 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=37 -GoCreateSyscall dt=1 new_g=4 -GoSyscallEndBlocked dt=1 -ProcStatus dt=1 p=0 pstatus=2 -ProcStart dt=1 p=0 p_seq=1 -GoStatus dt=1 g=4 m=18446744073709551615 gstatus=1 -GoStart dt=1 g=4 g_seq=1 -GoSyscallBegin dt=1 p_seq=2 stack=0 -GoDestroySyscall dt=1 -EventBatch gen=1 m=0 time=0 size=13 -ProcStatus dt=1 p=1 pstatus=2 -ProcStart dt=1 p=1 p_seq=1 -ProcSteal dt=1 p=0 p_seq=3 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-create-syscall-with-p.test b/src/internal/trace/v2/testdata/tests/go122-create-syscall-with-p.test deleted file mode 100644 index 9b329b8bae..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-create-syscall-with-p.test +++ /dev/null @@ -1,22 +0,0 @@ --- expect -- -FAILURE ".*expected a proc but didn't have one.*" --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=34 -GoCreateSyscall dt=1 new_g=4 -ProcStatus dt=1 p=0 pstatus=2 -ProcStart dt=1 p=0 p_seq=1 -GoSyscallEndBlocked dt=1 -GoStart dt=1 g=4 g_seq=1 -GoSyscallBegin dt=1 p_seq=2 stack=0 -GoDestroySyscall dt=1 -GoCreateSyscall dt=1 new_g=4 -GoSyscallEnd dt=1 -GoSyscallBegin dt=1 p_seq=3 stack=0 -GoDestroySyscall dt=1 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-fail-first-gen-first.test b/src/internal/trace/v2/testdata/tests/go122-fail-first-gen-first.test deleted file mode 100644 index cc4240de40..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-fail-first-gen-first.test +++ /dev/null @@ -1,9 +0,0 @@ --- expect -- -FAILURE "expected a proc but didn't have one" --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=0 time=0 size=5 -GoCreate dt=0 new_g=1 new_stack=0 stack=0 -EventBatch gen=2 m=0 time=0 size=50 diff --git a/src/internal/trace/v2/testdata/tests/go122-gc-stress.test b/src/internal/trace/v2/testdata/tests/go122-gc-stress.test deleted file mode 100644 index d5e7266f1e..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-gc-stress.test +++ /dev/null @@ -1,4207 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=3 m=18446744073709551615 time=28114950954550 size=5 -Frequency freq=15625000 -EventBatch gen=3 m=169438 time=28114950899454 size=615 -ProcStatus dt=2 p=47 pstatus=1 -GoStatus dt=1 g=111 m=169438 gstatus=2 -GCMarkAssistActive dt=1 g=111 -GCMarkAssistEnd dt=1 -HeapAlloc dt=38 heapalloc_value=191159744 -HeapAlloc dt=134 heapalloc_value=191192512 -GCMarkAssistBegin dt=60 stack=3 -GoStop dt=2288 reason_string=20 stack=9 -GoStart dt=15 g=111 g_seq=1 -GCMarkAssistEnd dt=1860 -HeapAlloc dt=46 heapalloc_value=191585728 -GCMarkAssistBegin dt=35 stack=3 -GoBlock dt=32 reason_string=13 stack=11 -GoUnblock dt=14 g=57 g_seq=5 stack=0 -GoStart dt=9 g=57 g_seq=6 -GoLabel dt=3 label_string=2 -GoBlock dt=2925 reason_string=15 stack=5 -GoUnblock dt=12 g=57 g_seq=7 stack=0 -GoStart dt=5 g=57 g_seq=8 -GoLabel dt=1 label_string=2 -GoBlock dt=391 reason_string=15 stack=5 -GoUnblock dt=15 g=57 g_seq=9 stack=0 -GoStart dt=7 g=57 g_seq=10 -GoLabel dt=1 label_string=2 -GoBlock dt=307 reason_string=15 stack=5 -GoUnblock dt=7 g=57 g_seq=11 stack=0 -GoStart dt=3 g=57 g_seq=12 -GoLabel dt=2 label_string=2 -GoBlock dt=1049 reason_string=15 stack=5 -GoUnblock dt=23 g=58 g_seq=7 stack=0 -GoStart dt=8 g=58 g_seq=8 -GoLabel dt=1 label_string=2 -GoBlock dt=1126 reason_string=15 stack=5 -GoUnblock dt=12 g=53 g_seq=3 stack=0 -GoStart dt=5 g=53 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=1751 reason_string=15 stack=5 -GoUnblock dt=12 g=53 g_seq=5 stack=0 -GoStart dt=6 g=53 g_seq=6 -GoLabel dt=3 label_string=2 -GoBlock dt=119 reason_string=15 stack=5 -GoStart dt=15 g=88 g_seq=4 -GoBlock dt=50 reason_string=13 stack=11 -GoUnblock dt=1212 g=54 g_seq=15 stack=0 -GoStart dt=6 g=54 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=2984 reason_string=15 stack=5 -GoUnblock dt=2696 g=52 g_seq=21 stack=0 -GoStart dt=3 g=52 g_seq=22 -GoLabel dt=1 label_string=4 -GoBlock dt=2013 reason_string=15 stack=5 -GoStart dt=18 g=98 g_seq=6 -GCMarkAssistEnd dt=6 -HeapAlloc dt=44 heapalloc_value=192003520 -GCMarkAssistBegin dt=54 stack=3 -GoBlock dt=481 reason_string=13 stack=11 -GoUnblock dt=51 g=14 g_seq=17 stack=0 -GoStart dt=4 g=14 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=3954 reason_string=15 stack=5 -GoUnblock dt=59 g=57 g_seq=41 stack=0 -GoStart dt=8 g=57 g_seq=42 -GoLabel dt=1 label_string=4 -GoBlock dt=63 reason_string=15 stack=5 -GoUnblock dt=11 g=57 g_seq=43 stack=0 -GoStart dt=4 g=57 g_seq=44 -GoLabel dt=1 label_string=2 -GoBlock dt=3186 reason_string=15 stack=5 -GoUnblock dt=11 g=57 g_seq=45 stack=0 -GoStart dt=3 g=57 g_seq=46 -GoLabel dt=1 label_string=2 -GoBlock dt=9 reason_string=15 stack=5 -ProcStop dt=60 -ProcStart dt=50 p=47 p_seq=1 -GoUnblock dt=9 g=22 g_seq=33 stack=0 -GoStart dt=4 g=22 g_seq=34 -GoLabel dt=1 label_string=4 -GoBlock dt=97 reason_string=15 stack=5 -GoUnblock dt=9 g=22 g_seq=35 stack=0 -GoStart dt=5 g=22 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=2605 reason_string=15 stack=5 -GoUnblock dt=10 g=22 g_seq=37 stack=0 -GoStart dt=4 g=22 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=13 reason_string=15 stack=5 -ProcStop dt=37 -ProcStart dt=582 p=47 p_seq=2 -GoStart dt=504 g=97 g_seq=4 -GCMarkAssistEnd dt=5 -GCMarkAssistBegin dt=34 stack=3 -GoBlock dt=279 reason_string=13 stack=11 -ProcStop dt=30 -ProcStart dt=3780 p=47 p_seq=3 -GoUnblock dt=9 g=71 g_seq=25 stack=0 -GoStart dt=128 g=71 g_seq=26 -GoLabel dt=1 label_string=2 -GoBlock dt=210 reason_string=15 stack=5 -GoUnblock dt=8 g=71 g_seq=27 stack=0 -GoStart dt=1 g=71 g_seq=28 -GoLabel dt=1 label_string=2 -GoBlock dt=1627 reason_string=15 stack=5 -GoStart dt=27 g=105 g_seq=6 -GCMarkAssistEnd dt=3 -HeapAlloc dt=44 heapalloc_value=192477912 -GCMarkAssistBegin dt=77 stack=3 -GoStop dt=873 reason_string=20 stack=9 -GoUnblock dt=12 g=23 g_seq=47 stack=0 -GoStart dt=3 g=23 g_seq=48 -GoLabel dt=1 label_string=2 -GoBlock dt=36 reason_string=15 stack=5 -GoUnblock dt=6 g=23 g_seq=49 stack=0 -GoStart dt=1 g=23 g_seq=50 -GoLabel dt=1 label_string=2 -GoBlock dt=9 reason_string=15 stack=5 -GoUnblock dt=8 g=23 g_seq=51 stack=0 -GoStart dt=3 g=23 g_seq=52 -GoLabel dt=1 label_string=2 -GoBlock dt=15 reason_string=15 stack=5 -GoStart dt=10 g=105 g_seq=7 -GoStop dt=16 reason_string=20 stack=9 -GoUnblock dt=7 g=23 g_seq=53 stack=0 -GoStart dt=3 g=23 g_seq=54 -GoLabel dt=1 label_string=2 -GoBlock dt=10 reason_string=15 stack=5 -GoUnblock dt=12 g=23 g_seq=55 stack=0 -GoStart dt=3 g=23 g_seq=56 -GoLabel dt=1 label_string=2 -GoBlock dt=9 reason_string=15 stack=5 -GoUnblock dt=4 g=23 g_seq=57 stack=0 -GoStart dt=1 g=23 g_seq=58 -GoLabel dt=1 label_string=2 -GoBlock dt=4554 reason_string=15 stack=5 -GoStart dt=14 g=105 g_seq=10 -GCMarkAssistEnd dt=5 -HeapAlloc dt=65 heapalloc_value=193682136 -GCMarkAssistBegin dt=16 stack=3 -GoBlock dt=44 reason_string=13 stack=11 -GoStart dt=15 g=83 g_seq=8 -HeapAlloc dt=221 heapalloc_value=194173656 -HeapAlloc dt=1927 heapalloc_value=195443416 -GoStop dt=5838 reason_string=16 stack=6 -GoStart dt=51 g=83 g_seq=9 -GCMarkAssistBegin dt=12 stack=3 -GoBlock dt=35 reason_string=10 stack=18 -GoStart dt=70 g=87 g_seq=6 -GCMarkAssistBegin dt=14 stack=3 -GoBlock dt=35 reason_string=13 stack=11 -ProcStop dt=77 -EventBatch gen=3 m=169436 time=28114950894898 size=160 -ProcStatus dt=2 p=34 pstatus=1 -GoStatus dt=3 g=107 m=169436 gstatus=2 -GCMarkAssistBegin dt=15 stack=3 -GoBlock dt=4050 reason_string=13 stack=11 -GoStatus dt=20 g=23 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=23 g_seq=1 stack=0 -GoStart dt=8 g=23 g_seq=2 -GoLabel dt=1 label_string=2 -GoUnblock dt=2316 g=81 g_seq=1 stack=12 -GoBlock dt=626 reason_string=15 stack=5 -GoUnblock dt=9 g=23 g_seq=3 stack=0 -GoStart dt=9 g=23 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=3975 reason_string=15 stack=5 -GoUnblock dt=35 g=23 g_seq=5 stack=0 -GoStart dt=6 g=23 g_seq=6 -GoLabel dt=1 label_string=2 -GoBlock dt=142 reason_string=15 stack=5 -GoUnblock dt=9 g=23 g_seq=7 stack=0 -GoStart dt=4 g=23 g_seq=8 -GoLabel dt=1 label_string=2 -GoBlock dt=3815 reason_string=15 stack=5 -GoUnblock dt=10 g=23 g_seq=9 stack=0 -GoStart dt=6 g=23 g_seq=10 -GoLabel dt=1 label_string=2 -GoBlock dt=3560 reason_string=15 stack=5 -GoUnblock dt=8 g=23 g_seq=11 stack=0 -GoStart dt=4 g=23 g_seq=12 -GoLabel dt=3 label_string=2 -GoBlock dt=2781 reason_string=15 stack=5 -GoUnblock dt=13 g=23 g_seq=13 stack=0 -GoStart dt=4 g=23 g_seq=14 -GoLabel dt=1 label_string=2 -GoBlock dt=1277 reason_string=15 stack=5 -ProcStop dt=16 -EventBatch gen=3 m=169435 time=28114950897148 size=522 -ProcStatus dt=2 p=24 pstatus=1 -GoStatus dt=2 g=122 m=169435 gstatus=2 -GCMarkAssistActive dt=1 g=122 -GCMarkAssistEnd dt=3 -HeapAlloc dt=24 heapalloc_value=190602688 -GCMarkAssistBegin dt=95 stack=3 -GCMarkAssistEnd dt=4651 -GCMarkAssistBegin dt=50 stack=3 -GoBlock dt=2931 reason_string=13 stack=11 -ProcStop dt=1401 -ProcStart dt=18 p=24 p_seq=1 -GoUnblock dt=3524 g=28 g_seq=5 stack=0 -GoStart dt=10 g=28 g_seq=6 -GoLabel dt=1 label_string=4 -GoBlock dt=42 reason_string=15 stack=5 -GoUnblock dt=1162 g=24 g_seq=11 stack=0 -GoStart dt=7 g=24 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=3050 reason_string=15 stack=5 -GoUnblock dt=5301 g=67 g_seq=15 stack=0 -GoStart dt=4 g=67 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=40 reason_string=15 stack=5 -ProcStop dt=64 -ProcStart dt=841 p=24 p_seq=2 -GoStatus dt=58 g=16 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=16 g_seq=1 stack=0 -GoStart dt=273 g=16 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=139 reason_string=15 stack=5 -ProcStop dt=52 -ProcStart dt=97 p=24 p_seq=3 -GoUnblock dt=5 g=16 g_seq=3 stack=0 -GoStart dt=2 g=16 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=471 reason_string=15 stack=5 -GoUnblock dt=58 g=16 g_seq=5 stack=0 -GoStart dt=6 g=16 g_seq=6 -GoLabel dt=3 label_string=4 -GoBlock dt=912 reason_string=15 stack=5 -GoUnblock dt=9 g=16 g_seq=7 stack=0 -GoStart dt=6 g=16 g_seq=8 -GoLabel dt=1 label_string=2 -GoUnblock dt=6571 g=113 g_seq=5 stack=12 -GoBlock dt=22 reason_string=15 stack=5 -ProcStop dt=73 -ProcStart dt=22914 p=30 p_seq=16 -GoStart dt=342 g=117 g_seq=4 -GCMarkAssistEnd dt=8 -HeapAlloc dt=67 heapalloc_value=196467152 -GoStop dt=5253 reason_string=16 stack=6 -GoStart dt=44 g=128 g_seq=7 -GCMarkAssistBegin dt=21 stack=3 -GoBlock dt=37 reason_string=10 stack=18 -GoStart dt=7 g=130 g_seq=5 -GoBlock dt=182 reason_string=10 stack=20 -ProcStop dt=81 -ProcStart dt=8287 p=2 p_seq=2 -GoStart dt=164 g=82 g_seq=11 -GCMarkAssistEnd dt=8 -HeapAlloc dt=169 heapalloc_value=104038048 -HeapAlloc dt=135 heapalloc_value=104189856 -HeapAlloc dt=126 heapalloc_value=104287136 -HeapAlloc dt=24 heapalloc_value=104308256 -HeapAlloc dt=28 heapalloc_value=104313888 -HeapAlloc dt=14 heapalloc_value=104399904 -GCSweepBegin dt=43 stack=28 -GCSweepEnd dt=8 swept_value=8192 reclaimed_value=8192 -HeapAlloc dt=4 heapalloc_value=104473632 -HeapAlloc dt=58 heapalloc_value=104510496 -HeapAlloc dt=22 heapalloc_value=104534432 -HeapAlloc dt=51 heapalloc_value=104654624 -GCSweepBegin dt=146 stack=28 -GCSweepEnd dt=8 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=4 heapalloc_value=104878624 -HeapAlloc dt=42 heapalloc_value=105007648 -HeapAlloc dt=29 heapalloc_value=105077280 -HeapAlloc dt=36 heapalloc_value=105105952 -HeapAlloc dt=44 heapalloc_value=105242784 -HeapAlloc dt=58 heapalloc_value=105431200 -HeapAlloc dt=128 heapalloc_value=105593760 -HeapAlloc dt=199 heapalloc_value=106209440 -GCSweepBegin dt=155 stack=28 -GCSweepEnd dt=13 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=106666272 -HeapAlloc dt=77 heapalloc_value=106901152 -HeapAlloc dt=64 heapalloc_value=107211808 -HeapAlloc dt=133 heapalloc_value=107661088 -HeapAlloc dt=34 heapalloc_value=107722528 -HeapAlloc dt=108 heapalloc_value=108207392 -GCSweepBegin dt=202 stack=28 -GCSweepEnd dt=13 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=108742816 -HeapAlloc dt=112 heapalloc_value=109093664 -HeapAlloc dt=207 heapalloc_value=109913120 -HeapAlloc dt=271 heapalloc_value=110834560 -HeapAlloc dt=212 heapalloc_value=111566720 -HeapAlloc dt=148 heapalloc_value=112190720 -HeapAlloc dt=74 heapalloc_value=112528128 -HeapAlloc dt=143 heapalloc_value=113050240 -HeapAlloc dt=19 heapalloc_value=113194368 -HeapAlloc dt=135 heapalloc_value=113615232 -GCSweepBegin dt=251 stack=27 -EventBatch gen=3 m=169434 time=28114950909315 size=660 -ProcStatus dt=2 p=7 pstatus=1 -GoStatus dt=2 g=71 m=169434 gstatus=2 -GoBlock dt=6 reason_string=15 stack=5 -GoUnblock dt=2633 g=53 g_seq=7 stack=0 -GoStart dt=7 g=53 g_seq=8 -GoLabel dt=3 label_string=4 -GoBlock dt=127 reason_string=15 stack=5 -GoUnblock dt=1358 g=52 g_seq=15 stack=0 -GoStart dt=7 g=52 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=27 reason_string=15 stack=5 -GoStart dt=1150 g=93 g_seq=4 -GCMarkAssistEnd dt=7 -HeapAlloc dt=39 heapalloc_value=191897024 -GCMarkAssistBegin dt=27 stack=3 -GoStop dt=894 reason_string=20 stack=9 -GoStart dt=13 g=93 g_seq=5 -GoBlock dt=150 reason_string=13 stack=11 -ProcStop dt=57 -ProcStart dt=14 p=7 p_seq=1 -ProcStop dt=4205 -ProcStart dt=18 p=7 p_seq=2 -GoUnblock dt=4 g=22 g_seq=17 stack=0 -GoStart dt=172 g=22 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=1298 reason_string=15 stack=5 -GoUnblock dt=12 g=22 g_seq=19 stack=0 -GoStart dt=7 g=22 g_seq=20 -GoLabel dt=1 label_string=2 -GoBlock dt=108 reason_string=15 stack=5 -GoUnblock dt=9 g=22 g_seq=21 stack=0 -GoStart dt=4 g=22 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=309 reason_string=15 stack=5 -GoUnblock dt=19 g=57 g_seq=35 stack=0 -GoStart dt=6 g=57 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=26 reason_string=15 stack=5 -GoUnblock dt=12 g=30 g_seq=15 stack=0 -GoStart dt=4 g=30 g_seq=16 -GoLabel dt=1 label_string=2 -GoBlock dt=410 reason_string=15 stack=5 -GoUnblock dt=2384 g=23 g_seq=37 stack=0 -GoStart dt=7 g=23 g_seq=38 -GoLabel dt=1 label_string=4 -GoBlock dt=119 reason_string=15 stack=5 -GoUnblock dt=58 g=25 g_seq=21 stack=0 -GoStart dt=4 g=25 g_seq=22 -GoLabel dt=1 label_string=4 -GoBlock dt=1875 reason_string=15 stack=5 -GoUnblock dt=53 g=29 g_seq=15 stack=0 -GoStart dt=3 g=29 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=133 reason_string=15 stack=5 -GoUnblock dt=51 g=25 g_seq=25 stack=0 -GoStart dt=5 g=25 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=14 reason_string=15 stack=5 -GoUnblock dt=42 g=25 g_seq=27 stack=0 -GoStart dt=3 g=25 g_seq=28 -GoLabel dt=1 label_string=4 -GoBlock dt=56 reason_string=15 stack=5 -GoUnblock dt=1741 g=24 g_seq=41 stack=0 -GoStart dt=4 g=24 g_seq=42 -GoLabel dt=3 label_string=2 -GoBlock dt=15 reason_string=15 stack=5 -GoUnblock dt=26 g=25 g_seq=31 stack=0 -GoStart dt=4 g=25 g_seq=32 -GoLabel dt=1 label_string=2 -GoBlock dt=2653 reason_string=15 stack=5 -GoUnblock dt=10 g=25 g_seq=33 stack=0 -GoStart dt=6 g=25 g_seq=34 -GoLabel dt=1 label_string=2 -GoBlock dt=151 reason_string=15 stack=5 -GoUnblock dt=37 g=25 g_seq=35 stack=0 -GoStart dt=3 g=25 g_seq=36 -GoLabel dt=1 label_string=4 -GoBlock dt=12 reason_string=15 stack=5 -GoUnblock dt=8 g=25 g_seq=37 stack=0 -GoStart dt=3 g=25 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=1197 reason_string=15 stack=5 -GoUnblock dt=38 g=22 g_seq=43 stack=0 -GoStart dt=7 g=22 g_seq=44 -GoLabel dt=1 label_string=4 -GoBlock dt=16 reason_string=15 stack=5 -ProcStop dt=28 -ProcStart dt=2728 p=7 p_seq=3 -GoUnblock dt=10 g=25 g_seq=39 stack=0 -GoStart dt=162 g=25 g_seq=40 -GoLabel dt=2 label_string=2 -GoBlock dt=36 reason_string=15 stack=5 -GoUnblock dt=10 g=25 g_seq=41 stack=0 -GoStart dt=4 g=25 g_seq=42 -GoLabel dt=1 label_string=2 -GoBlock dt=19 reason_string=15 stack=5 -GoUnblock dt=7 g=25 g_seq=43 stack=0 -GoStart dt=1 g=25 g_seq=44 -GoLabel dt=1 label_string=2 -GoUnblock dt=616 g=81 g_seq=6 stack=12 -GoBlock dt=1549 reason_string=15 stack=5 -GoStart dt=12 g=112 g_seq=5 -GoBlock dt=22 reason_string=13 stack=11 -GoStart dt=8 g=90 g_seq=4 -GCMarkAssistEnd dt=3 -HeapAlloc dt=2613 heapalloc_value=192625368 -GoStop dt=48 reason_string=16 stack=6 -GoUnblock dt=13 g=54 g_seq=35 stack=0 -GoStart dt=4 g=54 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=269 reason_string=15 stack=5 -GoUnblock dt=6 g=54 g_seq=37 stack=0 -GoStart dt=5 g=54 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=856 reason_string=15 stack=5 -GoUnblock dt=23 g=52 g_seq=61 stack=0 -GoStart dt=4 g=52 g_seq=62 -GoLabel dt=1 label_string=2 -GoBlock dt=33 reason_string=15 stack=5 -GoUnblock dt=13 g=52 g_seq=63 stack=0 -GoStart dt=2 g=52 g_seq=64 -GoLabel dt=1 label_string=2 -GoBlock dt=38 reason_string=15 stack=5 -GoUnblock dt=17 g=52 g_seq=65 stack=0 -GoStart dt=3 g=52 g_seq=66 -GoLabel dt=1 label_string=2 -GoBlock dt=37 reason_string=15 stack=5 -GoUnblock dt=11 g=52 g_seq=67 stack=0 -GoStart dt=4 g=52 g_seq=68 -GoLabel dt=1 label_string=2 -GoBlock dt=2457 reason_string=15 stack=5 -GoUnblock dt=11 g=52 g_seq=69 stack=0 -GoStart dt=4 g=52 g_seq=70 -GoLabel dt=1 label_string=2 -GoBlock dt=9 reason_string=15 stack=5 -GoStart dt=23 g=114 g_seq=4 -GCMarkAssistEnd dt=457 -HeapAlloc dt=223 heapalloc_value=194968280 -GoStop dt=6900 reason_string=16 stack=4 -GoStart dt=24 g=114 g_seq=5 -GCMarkAssistBegin dt=86 stack=3 -GoBlock dt=43 reason_string=10 stack=18 -ProcStop dt=49 -ProcStart dt=475 p=7 p_seq=4 -ProcStop dt=40 -ProcStart dt=1388 p=7 p_seq=5 -GoUnblock dt=9 g=131 g_seq=8 stack=0 -GoStart dt=169 g=131 g_seq=9 -GoSyscallBegin dt=24 p_seq=6 stack=7 -GoSyscallEnd dt=184 -GoBlock dt=11 reason_string=15 stack=2 -ProcStop dt=42 -ProcStart dt=18109 p=16 p_seq=2 -GoStart dt=176 g=91 g_seq=4 -GCMarkAssistEnd dt=8 -HeapAlloc dt=22 heapalloc_value=114837120 -HeapAlloc dt=88 heapalloc_value=114853504 -GCSweepBegin dt=145 stack=27 -EventBatch gen=3 m=169433 time=28114950897465 size=806 -ProcStatus dt=2 p=2 pstatus=1 -GoStatus dt=1 g=24 m=169433 gstatus=2 -GoBlock dt=9 reason_string=15 stack=5 -GoUnblock dt=19 g=24 g_seq=1 stack=0 -GoStart dt=5 g=24 g_seq=2 -GoLabel dt=2 label_string=2 -GoBlock dt=4044 reason_string=15 stack=5 -GoUnblock dt=17 g=24 g_seq=3 stack=0 -GoStart dt=4 g=24 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=4262 reason_string=15 stack=5 -GoUnblock dt=19 g=28 g_seq=3 stack=0 -GoStart dt=4 g=28 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=461 reason_string=15 stack=5 -GoUnblock dt=4544 g=72 g_seq=15 stack=0 -GoStart dt=9 g=72 g_seq=16 -GoLabel dt=3 label_string=4 -GoBlock dt=32 reason_string=15 stack=5 -GoUnblock dt=9 g=72 g_seq=17 stack=0 -GoStart dt=2 g=72 g_seq=18 -GoLabel dt=2 label_string=2 -GoBlock dt=13 reason_string=15 stack=5 -GoUnblock dt=3 g=72 g_seq=19 stack=0 -GoStart dt=1 g=72 g_seq=20 -GoLabel dt=1 label_string=2 -GoBlock dt=237 reason_string=15 stack=5 -GoUnblock dt=8 g=72 g_seq=21 stack=0 -GoStart dt=3 g=72 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=151 reason_string=15 stack=5 -GoUnblock dt=11 g=72 g_seq=23 stack=0 -GoStart dt=6 g=72 g_seq=24 -GoLabel dt=1 label_string=2 -GoBlock dt=3418 reason_string=15 stack=5 -ProcStop dt=1573 -ProcStart dt=17 p=2 p_seq=1 -ProcStop dt=1102 -ProcStart dt=21668 p=19 p_seq=4 -GoUnblock dt=16 g=51 g_seq=47 stack=0 -GoStart dt=7 g=51 g_seq=48 -GoLabel dt=1 label_string=2 -GoBlock dt=60 reason_string=15 stack=5 -GoUnblock dt=6 g=51 g_seq=49 stack=0 -GoStart dt=1 g=51 g_seq=50 -GoLabel dt=3 label_string=2 -GoBlock dt=5166 reason_string=15 stack=5 -GoStart dt=18 g=106 g_seq=5 -GCMarkAssistEnd dt=10 -HeapAlloc dt=56 heapalloc_value=193452760 -GCMarkAssistBegin dt=116 stack=3 -GCMarkAssistEnd dt=58 -HeapAlloc dt=47 heapalloc_value=193714904 -GoStop dt=54 reason_string=16 stack=6 -GoUnblock dt=18 g=54 g_seq=41 stack=0 -GoStart dt=4 g=54 g_seq=42 -GoLabel dt=2 label_string=2 -GoUnblock dt=16 g=105 g_seq=11 stack=12 -GoBlock dt=21 reason_string=15 stack=5 -GoStart dt=8 g=105 g_seq=12 -GCMarkAssistEnd dt=7 -HeapAlloc dt=33 heapalloc_value=193919704 -GCMarkAssistBegin dt=13 stack=3 -GCMarkAssistEnd dt=91 -HeapAlloc dt=173 heapalloc_value=194378456 -GCMarkAssistBegin dt=26 stack=3 -GoBlock dt=37 reason_string=13 stack=11 -GoStart dt=33 g=104 g_seq=2 -GCMarkAssistEnd dt=5 -HeapAlloc dt=81 heapalloc_value=194673368 -GoStop dt=2248 reason_string=16 stack=6 -GoStart dt=2855 g=104 g_seq=3 -GCMarkAssistBegin dt=16 stack=3 -GoBlock dt=27 reason_string=10 stack=18 -GoStart dt=16 g=103 g_seq=5 -GCMarkAssistEnd dt=6 -HeapAlloc dt=6180 heapalloc_value=196655568 -GoStop dt=14 reason_string=16 stack=6 -GoStart dt=146 g=102 g_seq=5 -GCMarkAssistBegin dt=10 stack=3 -HeapAlloc dt=38 heapalloc_value=196663760 -GoBlock dt=16 reason_string=10 stack=18 -ProcStop dt=41 -ProcStart dt=1317 p=19 p_seq=5 -ProcStop dt=24 -ProcStart dt=2117 p=0 p_seq=5 -GoStart dt=5190 g=115 g_seq=10 -GCMarkAssistEnd dt=6 -GCSweepBegin dt=22 stack=27 -GCSweepEnd dt=727 swept_value=71303168 reclaimed_value=1302272 -HeapAlloc dt=37 heapalloc_value=103898784 -HeapAlloc dt=200 heapalloc_value=103947936 -HeapAlloc dt=63 heapalloc_value=103960224 -HeapAlloc dt=27 heapalloc_value=103997088 -HeapAlloc dt=65 heapalloc_value=104103584 -HeapAlloc dt=87 heapalloc_value=104132512 -HeapAlloc dt=63 heapalloc_value=104255392 -HeapAlloc dt=87 heapalloc_value=104267680 -HeapAlloc dt=73 heapalloc_value=104379424 -HeapAlloc dt=79 heapalloc_value=104494112 -GCSweepBegin dt=40 stack=28 -GCSweepEnd dt=7 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=8 heapalloc_value=104526880 -HeapAlloc dt=27 heapalloc_value=104589088 -HeapAlloc dt=42 heapalloc_value=104711968 -HeapAlloc dt=83 heapalloc_value=104821280 -GCSweepBegin dt=21 stack=28 -GCSweepEnd dt=4 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=2 heapalloc_value=104854048 -HeapAlloc dt=105 heapalloc_value=105064992 -GCSweepBegin dt=94 stack=28 -GCSweepEnd dt=9 swept_value=8192 reclaimed_value=8192 -HeapAlloc dt=4 heapalloc_value=105250976 -GCSweepBegin dt=29 stack=28 -GCSweepEnd dt=10 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=4 heapalloc_value=105447584 -HeapAlloc dt=30 heapalloc_value=105476256 -HeapAlloc dt=57 heapalloc_value=105566368 -GCSweepBegin dt=74 stack=28 -GCSweepEnd dt=5 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=105741216 -HeapAlloc dt=77 heapalloc_value=105921440 -HeapAlloc dt=76 heapalloc_value=106143904 -HeapAlloc dt=50 heapalloc_value=106274976 -HeapAlloc dt=113 heapalloc_value=106633504 -HeapAlloc dt=110 heapalloc_value=107036320 -HeapAlloc dt=95 heapalloc_value=107351072 -HeapAlloc dt=80 heapalloc_value=107702048 -GCSweepBegin dt=78 stack=28 -GCSweepEnd dt=6 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=2 heapalloc_value=107835936 -HeapAlloc dt=39 heapalloc_value=107904288 -HeapAlloc dt=82 heapalloc_value=108390432 -HeapAlloc dt=230 heapalloc_value=108955808 -HeapAlloc dt=126 heapalloc_value=109421344 -GCSweepBegin dt=131 stack=28 -GCSweepEnd dt=5 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=3 heapalloc_value=109929504 -GCSweepBegin dt=29 stack=28 -GCSweepEnd dt=4 swept_value=8192 reclaimed_value=8192 -HeapAlloc dt=3 heapalloc_value=110038816 -HeapAlloc dt=28 heapalloc_value=110109472 -HeapAlloc dt=93 heapalloc_value=110412672 -HeapAlloc dt=33 heapalloc_value=110547840 -HeapAlloc dt=123 heapalloc_value=111070848 -GCSweepBegin dt=155 stack=28 -GCSweepEnd dt=10 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=3 heapalloc_value=111648640 -GCSweepBegin dt=61 stack=28 -GCSweepEnd dt=8 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=3 heapalloc_value=111996800 -GCSweepBegin dt=37 stack=28 -GCSweepEnd dt=5 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=8 heapalloc_value=112149760 -HeapAlloc dt=32 heapalloc_value=112342272 -GCSweepBegin dt=75 stack=28 -GCSweepEnd dt=7 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=5 heapalloc_value=112601856 -HeapAlloc dt=61 heapalloc_value=112923264 -HeapAlloc dt=90 heapalloc_value=113262720 -HeapAlloc dt=88 heapalloc_value=113522304 -HeapAlloc dt=119 heapalloc_value=113967488 -HeapAlloc dt=59 heapalloc_value=114201216 -GCSweepBegin dt=130 stack=27 -EventBatch gen=3 m=169431 time=28114950897743 size=407 -ProcStatus dt=2 p=11 pstatus=1 -GoStatus dt=4 g=51 m=169431 gstatus=2 -GoBlock dt=6 reason_string=15 stack=5 -GoUnblock dt=13 g=51 g_seq=1 stack=0 -GoStart dt=6 g=51 g_seq=2 -GoLabel dt=1 label_string=2 -GoBlock dt=4143 reason_string=15 stack=5 -GoStatus dt=1425 g=28 m=18446744073709551615 gstatus=4 -GoUnblock dt=2 g=28 g_seq=1 stack=0 -GoStart dt=7 g=28 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=1758 reason_string=15 stack=5 -GoUnblock dt=3904 g=25 g_seq=9 stack=0 -GoStart dt=9 g=25 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=41 reason_string=15 stack=5 -ProcStop dt=1189 -ProcStart dt=16 p=11 p_seq=1 -GoUnblock dt=1157 g=57 g_seq=21 stack=0 -GoStart dt=6 g=57 g_seq=22 -GoLabel dt=1 label_string=4 -GoBlock dt=25 reason_string=15 stack=5 -GoUnblock dt=1614 g=52 g_seq=13 stack=0 -GoStart dt=11 g=52 g_seq=14 -GoLabel dt=4 label_string=4 -GoBlock dt=86 reason_string=15 stack=5 -GoUnblock dt=4771 g=22 g_seq=11 stack=0 -GoStart dt=12 g=22 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=1413 reason_string=15 stack=5 -GoUnblock dt=10 g=22 g_seq=13 stack=0 -GoStart dt=4 g=22 g_seq=14 -GoLabel dt=1 label_string=2 -GoBlock dt=39 reason_string=15 stack=5 -ProcStop dt=67 -ProcStart dt=2286 p=11 p_seq=2 -ProcStop dt=95 -ProcStart dt=53 p=0 p_seq=2 -GoUnblock dt=9 g=57 g_seq=33 stack=0 -GoStart dt=8 g=57 g_seq=34 -GoLabel dt=1 label_string=4 -GoBlock dt=37 reason_string=15 stack=5 -GoUnblock dt=20 g=22 g_seq=23 stack=0 -GoStart dt=3 g=22 g_seq=24 -GoLabel dt=1 label_string=2 -GoBlock dt=1036 reason_string=15 stack=5 -GoUnblock dt=11 g=22 g_seq=25 stack=0 -GoStart dt=6 g=22 g_seq=26 -GoLabel dt=1 label_string=2 -GoBlock dt=2130 reason_string=15 stack=5 -GoUnblock dt=11 g=22 g_seq=27 stack=0 -GoStart dt=7 g=22 g_seq=28 -GoLabel dt=2 label_string=2 -GoBlock dt=1227 reason_string=15 stack=5 -GoUnblock dt=12 g=22 g_seq=29 stack=0 -GoStart dt=6 g=22 g_seq=30 -GoLabel dt=1 label_string=2 -GoBlock dt=31 reason_string=15 stack=5 -GoUnblock dt=7 g=22 g_seq=31 stack=0 -GoStart dt=2 g=22 g_seq=32 -GoLabel dt=1 label_string=2 -GoBlock dt=2282 reason_string=15 stack=5 -GoUnblock dt=71 g=29 g_seq=33 stack=0 -GoStart dt=4 g=29 g_seq=34 -GoLabel dt=1 label_string=4 -GoBlock dt=1234 reason_string=15 stack=5 -GoUnblock dt=8 g=29 g_seq=35 stack=0 -GoStart dt=8 g=29 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=18 reason_string=15 stack=5 -ProcStop dt=49 -ProcStart dt=10623 p=11 p_seq=5 -ProcStop dt=54 -ProcStart dt=686 p=11 p_seq=6 -GoStart dt=185 g=127 g_seq=5 -GCMarkAssistBegin dt=71 stack=3 -GoStop dt=67 reason_string=20 stack=9 -GoUnblock dt=15 g=53 g_seq=47 stack=0 -GoStart dt=3 g=53 g_seq=48 -GoLabel dt=1 label_string=2 -GoUnblock dt=661 g=121 g_seq=10 stack=12 -GoUnblock dt=7 g=88 g_seq=5 stack=12 -GoUnblock dt=8 g=87 g_seq=4 stack=12 -GoUnblock dt=2751 g=94 g_seq=10 stack=12 -GoUnblock dt=8 g=106 g_seq=7 stack=12 -GoUnblock dt=8 g=98 g_seq=9 stack=12 -GoBlock dt=18 reason_string=15 stack=5 -GoStart dt=17 g=87 g_seq=5 -GCMarkAssistEnd dt=5 -HeapAlloc dt=202 heapalloc_value=194796248 -GoStop dt=7327 reason_string=16 stack=6 -GoStart dt=68 g=84 g_seq=8 -GCMarkAssistBegin dt=16 stack=3 -GoBlock dt=29 reason_string=13 stack=11 -ProcStop dt=88 -EventBatch gen=3 m=169428 time=28114950899204 size=756 -ProcStatus dt=2 p=31 pstatus=1 -GoStatus dt=5 g=104 m=169428 gstatus=2 -GCMarkAssistActive dt=1 g=104 -GCMarkAssistEnd dt=2 -HeapAlloc dt=37 heapalloc_value=191110592 -GCMarkAssistBegin dt=21 stack=3 -GoBlock dt=2670 reason_string=13 stack=11 -GoStatus dt=1400 g=22 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=22 g_seq=1 stack=0 -GoStart dt=7 g=22 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=43 reason_string=15 stack=5 -GoUnblock dt=2567 g=70 g_seq=3 stack=0 -GoStart dt=9 g=70 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=329 reason_string=15 stack=5 -GoUnblock dt=97 g=70 g_seq=5 stack=0 -GoStart dt=5 g=70 g_seq=6 -GoLabel dt=3 label_string=2 -GoUnblock dt=1728 g=84 g_seq=3 stack=12 -GoBlock dt=3527 reason_string=15 stack=5 -GoStart dt=4132 g=114 g_seq=2 -GoStatus dt=28 g=115 m=18446744073709551615 gstatus=4 -GoUnblock dt=8 g=115 g_seq=1 stack=10 -GCMarkAssistBegin dt=18 stack=3 -GoBlock dt=196 reason_string=13 stack=11 -GoStart dt=14 g=115 g_seq=2 -GoStatus dt=18 g=102 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=102 g_seq=1 stack=10 -GCMarkAssistBegin dt=13 stack=3 -GoBlock dt=371 reason_string=13 stack=11 -GoUnblock dt=9 g=30 g_seq=11 stack=0 -GoStart dt=6 g=30 g_seq=12 -GoLabel dt=1 label_string=2 -GoBlock dt=5520 reason_string=15 stack=5 -GoUnblock dt=8 g=30 g_seq=13 stack=0 -GoStart dt=4 g=30 g_seq=14 -GoLabel dt=1 label_string=2 -GoBlock dt=28 reason_string=15 stack=5 -GoUnblock dt=10 g=57 g_seq=37 stack=0 -GoStart dt=3 g=57 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=157 reason_string=15 stack=5 -GoUnblock dt=7 g=57 g_seq=39 stack=0 -GoStart dt=4 g=57 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=140 reason_string=15 stack=5 -GoUnblock dt=10 g=53 g_seq=25 stack=0 -GoStart dt=3 g=53 g_seq=26 -GoLabel dt=1 label_string=2 -GoBlock dt=90 reason_string=15 stack=5 -GoUnblock dt=62 g=53 g_seq=27 stack=0 -GoStart dt=4 g=53 g_seq=28 -GoLabel dt=1 label_string=4 -GoBlock dt=11 reason_string=15 stack=5 -GoUnblock dt=46 g=53 g_seq=29 stack=0 -GoStart dt=7 g=53 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=51 reason_string=15 stack=5 -ProcStop dt=2236 -ProcStart dt=966 p=35 p_seq=2 -GoStart dt=19 g=81 g_seq=5 -GCMarkAssistEnd dt=7 -HeapAlloc dt=67 heapalloc_value=192133920 -GCMarkAssistBegin dt=46 stack=3 -GoBlock dt=32 reason_string=13 stack=11 -ProcStop dt=57 -ProcStart dt=15 p=35 p_seq=3 -GoUnblock dt=2 g=69 g_seq=23 stack=0 -GoStart dt=2 g=69 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=224 reason_string=15 stack=5 -GoUnblock dt=52 g=69 g_seq=25 stack=0 -GoStart dt=3 g=69 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=289 reason_string=15 stack=5 -GoStart dt=23 g=118 g_seq=2 -GCMarkAssistEnd dt=7 -HeapAlloc dt=21 heapalloc_value=192207648 -GCMarkAssistBegin dt=103 stack=3 -GoBlock dt=18 reason_string=13 stack=11 -GoUnblock dt=48 g=29 g_seq=13 stack=0 -GoStart dt=1 g=29 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=19 reason_string=15 stack=5 -GoUnblock dt=44 g=25 g_seq=23 stack=0 -GoStart dt=6 g=25 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=144 reason_string=15 stack=5 -GoUnblock dt=49 g=29 g_seq=17 stack=0 -GoStart dt=1 g=29 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=777 reason_string=15 stack=5 -GoUnblock dt=56 g=52 g_seq=31 stack=0 -GoStart dt=3 g=52 g_seq=32 -GoLabel dt=1 label_string=4 -GoBlock dt=21 reason_string=15 stack=5 -GoUnblock dt=27 g=51 g_seq=33 stack=0 -GoStart dt=5 g=51 g_seq=34 -GoLabel dt=1 label_string=2 -GoBlock dt=12 reason_string=15 stack=5 -GoUnblock dt=13 g=51 g_seq=35 stack=0 -GoStart dt=4 g=51 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=226 reason_string=15 stack=5 -GoUnblock dt=7 g=51 g_seq=37 stack=0 -GoStart dt=4 g=51 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=3928 reason_string=15 stack=5 -GoUnblock dt=14 g=51 g_seq=39 stack=0 -GoStart dt=3 g=51 g_seq=40 -GoLabel dt=3 label_string=2 -GoBlock dt=214 reason_string=15 stack=5 -GoUnblock dt=5 g=51 g_seq=41 stack=0 -GoStart dt=1 g=51 g_seq=42 -GoLabel dt=1 label_string=2 -GoBlock dt=305 reason_string=15 stack=5 -GoUnblock dt=8 g=51 g_seq=43 stack=0 -GoStart dt=5 g=51 g_seq=44 -GoLabel dt=1 label_string=2 -GoBlock dt=9 reason_string=15 stack=5 -ProcStop dt=47 -ProcStart dt=5058 p=35 p_seq=4 -GoUnblock dt=20 g=52 g_seq=51 stack=0 -GoStart dt=188 g=52 g_seq=52 -GoLabel dt=1 label_string=2 -GoBlock dt=33 reason_string=15 stack=5 -GoUnblock dt=9 g=52 g_seq=53 stack=0 -GoStart dt=4 g=52 g_seq=54 -GoLabel dt=1 label_string=2 -GoBlock dt=12 reason_string=15 stack=5 -GoStart dt=14 g=126 g_seq=3 -GCMarkAssistEnd dt=7 -HeapAlloc dt=2068 heapalloc_value=192592600 -GoStop dt=31 reason_string=16 stack=4 -GoUnblock dt=10 g=30 g_seq=39 stack=0 -GoStart dt=4 g=30 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=54 reason_string=15 stack=5 -GoStart dt=708 g=121 g_seq=9 -GoBlock dt=49 reason_string=13 stack=11 -GoStart dt=18 g=90 g_seq=5 -GCMarkAssistBegin dt=12 stack=3 -GoBlock dt=39 reason_string=13 stack=11 -GoUnblock dt=15 g=71 g_seq=31 stack=0 -GoStart dt=4 g=71 g_seq=32 -GoLabel dt=2 label_string=2 -GoBlock dt=1101 reason_string=15 stack=5 -GoUnblock dt=13 g=71 g_seq=33 stack=0 -GoStart dt=4 g=71 g_seq=34 -GoLabel dt=1 label_string=2 -GoBlock dt=27 reason_string=15 stack=5 -GoUnblock dt=18 g=14 g_seq=54 stack=0 -GoStart dt=4 g=14 g_seq=55 -GoLabel dt=2 label_string=2 -GoUnblock dt=2171 g=94 g_seq=8 stack=12 -GoBlock dt=28 reason_string=15 stack=5 -GoStart dt=11 g=94 g_seq=9 -GCMarkAssistEnd dt=6 -HeapAlloc dt=42 heapalloc_value=193665752 -GCMarkAssistBegin dt=100 stack=3 -GoBlock dt=30 reason_string=13 stack=11 -GoStart dt=13 g=106 g_seq=6 -HeapAlloc dt=99 heapalloc_value=193977048 -GCMarkAssistBegin dt=21 stack=3 -GoBlock dt=30 reason_string=13 stack=11 -GoStart dt=16 g=92 g_seq=4 -GCMarkAssistEnd dt=6 -HeapAlloc dt=884 heapalloc_value=195205848 -GoStop dt=3270 reason_string=16 stack=4 -GoStart dt=29 g=97 g_seq=6 -GCMarkAssistEnd dt=6 -HeapAlloc dt=42 heapalloc_value=195795408 -GCMarkAssistBegin dt=3026 stack=3 -GoBlock dt=85 reason_string=10 stack=18 -ProcStop dt=99 -EventBatch gen=3 m=169426 time=28114950897488 size=116 -ProcStatus dt=1 p=32 pstatus=1 -GoStatus dt=2 g=90 m=169426 gstatus=2 -GCMarkAssistActive dt=1 g=90 -GCMarkAssistEnd dt=3 -HeapAlloc dt=47 heapalloc_value=190627264 -GCMarkAssistBegin dt=51 stack=3 -GoBlock dt=2393 reason_string=13 stack=11 -GoStart dt=1449 g=125 g_seq=2 -GoStatus dt=26 g=127 m=18446744073709551615 gstatus=4 -GoUnblock dt=16 g=127 g_seq=1 stack=10 -GCMarkAssistBegin dt=17 stack=3 -GoStop dt=6909 reason_string=20 stack=9 -GoStart dt=20 g=125 g_seq=3 -GoBlock dt=2101 reason_string=13 stack=11 -GoUnblock dt=2575 g=71 g_seq=11 stack=0 -GoStart dt=8 g=71 g_seq=12 -GoLabel dt=3 label_string=4 -GoBlock dt=44 reason_string=15 stack=5 -GoStart dt=20 g=82 g_seq=7 -GoBlock dt=367 reason_string=13 stack=11 -GoUnblock dt=11 g=22 g_seq=9 stack=0 -GoStart dt=4 g=22 g_seq=10 -GoLabel dt=1 label_string=2 -GoBlock dt=3492 reason_string=15 stack=5 -ProcStop dt=9 -EventBatch gen=3 m=169425 time=28114950900302 size=349 -ProcStatus dt=2 p=10 pstatus=1 -GoStatus dt=2 g=70 m=169425 gstatus=2 -GoBlock dt=8 reason_string=15 stack=5 -GoUnblock dt=15 g=70 g_seq=1 stack=0 -GoStart dt=5 g=70 g_seq=2 -GoLabel dt=1 label_string=2 -GoBlock dt=5604 reason_string=15 stack=5 -GoUnblock dt=33 g=29 g_seq=3 stack=0 -GoStart dt=5 g=29 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=1191 reason_string=15 stack=5 -GoUnblock dt=9 g=29 g_seq=5 stack=0 -GoStart dt=5 g=29 g_seq=6 -GoLabel dt=2 label_string=2 -GoBlock dt=1935 reason_string=15 stack=5 -GoUnblock dt=15 g=51 g_seq=11 stack=0 -GoStart dt=5 g=51 g_seq=12 -GoLabel dt=1 label_string=2 -GoBlock dt=307 reason_string=15 stack=5 -ProcStop dt=4189 -ProcStart dt=15 p=10 p_seq=1 -GoUnblock dt=10 g=69 g_seq=17 stack=0 -GoStart dt=4 g=69 g_seq=18 -GoLabel dt=1 label_string=2 -GoUnblock dt=780 g=93 g_seq=3 stack=12 -GoBlock dt=6076 reason_string=15 stack=5 -GoUnblock dt=11 g=69 g_seq=19 stack=0 -GoStart dt=4 g=69 g_seq=20 -GoLabel dt=3 label_string=2 -GoUnblock dt=93 g=98 g_seq=5 stack=12 -GoBlock dt=5034 reason_string=15 stack=5 -GoUnblock dt=14 g=58 g_seq=25 stack=0 -GoStart dt=5 g=58 g_seq=26 -GoLabel dt=2 label_string=2 -GoBlock dt=1253 reason_string=15 stack=5 -GoUnblock dt=6 g=58 g_seq=27 stack=0 -GoStart dt=4 g=58 g_seq=28 -GoLabel dt=1 label_string=2 -GoBlock dt=1031 reason_string=15 stack=5 -GoUnblock dt=6 g=58 g_seq=29 stack=0 -GoStart dt=4 g=58 g_seq=30 -GoLabel dt=1 label_string=2 -GoBlock dt=17 reason_string=15 stack=5 -GoUnblock dt=24 g=52 g_seq=33 stack=0 -GoStart dt=6 g=52 g_seq=34 -GoLabel dt=1 label_string=2 -GoBlock dt=321 reason_string=15 stack=5 -GoUnblock dt=75 g=29 g_seq=27 stack=0 -GoStart dt=4 g=29 g_seq=28 -GoLabel dt=1 label_string=4 -GoBlock dt=248 reason_string=15 stack=5 -GoUnblock dt=61 g=57 g_seq=47 stack=0 -GoStart dt=4 g=57 g_seq=48 -GoLabel dt=1 label_string=4 -GoBlock dt=794 reason_string=15 stack=5 -ProcStop dt=41 -ProcStart dt=15678 p=21 p_seq=3 -GoStart dt=22 g=88 g_seq=6 -GCMarkAssistEnd dt=9 -HeapAlloc dt=84 heapalloc_value=194730712 -GoStop dt=2177 reason_string=16 stack=6 -GoStart dt=2495 g=88 g_seq=7 -GCMarkAssistBegin dt=19 stack=3 -GoBlock dt=37 reason_string=10 stack=18 -GoStart dt=15 g=126 g_seq=6 -GCMarkAssistEnd dt=5 -HeapAlloc dt=27 heapalloc_value=196114896 -GCMarkAssistBegin dt=18 stack=3 -GoBlock dt=30 reason_string=10 stack=18 -GoStart dt=15 g=98 g_seq=10 -GCMarkAssistEnd dt=6 -HeapAlloc dt=48 heapalloc_value=196155856 -GoStop dt=6168 reason_string=16 stack=6 -GoStart dt=156 g=98 g_seq=11 -GCMarkAssistBegin dt=9 stack=3 -GoBlock dt=27 reason_string=10 stack=18 -GoStart dt=27 g=94 g_seq=12 -GCMarkAssistBegin dt=13 stack=3 -GoBlock dt=35 reason_string=10 stack=18 -ProcStop dt=55 -ProcStart dt=14725 p=13 p_seq=1 -GoStart dt=171 g=112 g_seq=9 -GCMarkAssistEnd dt=6 -GCSweepBegin dt=222 stack=27 -EventBatch gen=3 m=169424 time=28114950894869 size=176 -ProcStatus dt=1 p=23 pstatus=3 -GoStatus dt=2 g=131 m=169424 gstatus=3 -GoSyscallEnd dt=1 -GoBlock dt=64 reason_string=15 stack=2 -GoUnblock dt=2260 g=67 g_seq=1 stack=0 -GoStart dt=6 g=67 g_seq=2 -GoLabel dt=2 label_string=4 -GoBlock dt=530 reason_string=15 stack=5 -GoUnblock dt=12 g=69 g_seq=3 stack=0 -GoStart dt=5 g=69 g_seq=4 -GoLabel dt=1 label_string=2 -GoUnblock dt=2455 g=90 g_seq=1 stack=12 -GoBlock dt=20 reason_string=15 stack=5 -GoUnblock dt=16 g=69 g_seq=5 stack=0 -GoStart dt=7 g=69 g_seq=6 -GoLabel dt=1 label_string=2 -GoUnblock dt=493 g=120 g_seq=2 stack=12 -GoBlock dt=1777 reason_string=15 stack=5 -GoUnblock dt=832 g=69 g_seq=7 stack=0 -GoStart dt=9 g=69 g_seq=8 -GoLabel dt=2 label_string=2 -GoBlock dt=5425 reason_string=15 stack=5 -GoUnblock dt=15 g=69 g_seq=9 stack=0 -GoStart dt=4 g=69 g_seq=10 -GoLabel dt=1 label_string=2 -GoBlock dt=1370 reason_string=15 stack=5 -ProcStop dt=2533 -ProcStart dt=11 p=23 p_seq=1 -GoUnblock dt=3354 g=54 g_seq=17 stack=0 -GoStart dt=7 g=54 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=29 reason_string=15 stack=5 -GoUnblock dt=25 g=54 g_seq=19 stack=0 -GoStart dt=5 g=54 g_seq=20 -GoLabel dt=2 label_string=2 -GoBlock dt=232 reason_string=15 stack=5 -GoUnblock dt=11 g=54 g_seq=21 stack=0 -GoStart dt=7 g=54 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=293 reason_string=15 stack=5 -ProcStop dt=11 -EventBatch gen=3 m=169423 time=28114950894865 size=525 -ProcStatus dt=2 p=30 pstatus=1 -GoStatus dt=4 g=87 m=169423 gstatus=2 -GCMarkAssistActive dt=1 g=87 -GCMarkAssistEnd dt=2 -HeapAlloc dt=98 heapalloc_value=189963712 -GoStop dt=56 reason_string=16 stack=6 -GoUnblock dt=20 g=131 g_seq=1 stack=0 -GoStart dt=7 g=131 g_seq=2 -GoSyscallBegin dt=39 p_seq=1 stack=7 -GoSyscallEnd dt=304 -GoSyscallBegin dt=19 p_seq=2 stack=7 -GoSyscallEnd dt=59 -GoSyscallBegin dt=15 p_seq=3 stack=7 -GoSyscallEnd dt=52 -GoSyscallBegin dt=11 p_seq=4 stack=7 -GoSyscallEnd dt=50 -GoSyscallBegin dt=8 p_seq=5 stack=7 -GoSyscallEnd dt=48 -GoSyscallBegin dt=10 p_seq=6 stack=7 -GoSyscallEnd dt=54 -GoSyscallBegin dt=13 p_seq=7 stack=7 -GoSyscallEnd dt=51 -GoSyscallBegin dt=12 p_seq=8 stack=7 -GoSyscallEnd dt=49 -GoSyscallBegin dt=16 p_seq=9 stack=7 -GoSyscallEnd dt=245 -GoSyscallBegin dt=12 p_seq=10 stack=7 -GoSyscallEnd dt=49 -GoSyscallBegin dt=10 p_seq=11 stack=7 -GoSyscallEnd dt=49 -GoSyscallBegin dt=10 p_seq=12 stack=7 -GoSyscallEnd dt=48 -GoSyscallBegin dt=6 p_seq=13 stack=7 -GoSyscallEnd dt=52 -GoStop dt=24 reason_string=16 stack=8 -GoUnblock dt=9 g=14 g_seq=1 stack=0 -GoStart dt=5 g=14 g_seq=2 -GoLabel dt=1 label_string=2 -GoUnblock dt=2948 g=107 g_seq=1 stack=12 -GoBlock dt=2891 reason_string=15 stack=5 -GoUnblock dt=11 g=14 g_seq=3 stack=0 -GoStart dt=5 g=14 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=1138 reason_string=15 stack=5 -GoUnblock dt=22 g=51 g_seq=5 stack=0 -GoStart dt=5 g=51 g_seq=6 -GoLabel dt=1 label_string=2 -GoUnblock dt=451 g=82 g_seq=3 stack=12 -GoBlock dt=460 reason_string=15 stack=5 -GoUnblock dt=4052 g=54 g_seq=5 stack=0 -GoStart dt=11 g=54 g_seq=6 -GoLabel dt=1 label_string=4 -GoBlock dt=72 reason_string=15 stack=5 -GoUnblock dt=1333 g=57 g_seq=15 stack=0 -GoStart dt=8 g=57 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=283 reason_string=15 stack=5 -GoUnblock dt=1185 g=57 g_seq=19 stack=0 -GoStart dt=7 g=57 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=134 reason_string=15 stack=5 -GoUnblock dt=1144 g=53 g_seq=11 stack=0 -GoStart dt=6 g=53 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=372 reason_string=15 stack=5 -GoUnblock dt=16 g=53 g_seq=13 stack=0 -GoStart dt=7 g=53 g_seq=14 -GoLabel dt=1 label_string=2 -GoBlock dt=8581 reason_string=15 stack=5 -ProcStop dt=76 -ProcStart dt=22 p=30 p_seq=14 -GoUnblock dt=3 g=72 g_seq=31 stack=0 -GoStart dt=7 g=72 g_seq=32 -GoLabel dt=1 label_string=4 -GoBlock dt=46 reason_string=15 stack=5 -GoUnblock dt=63 g=23 g_seq=31 stack=0 -GoStart dt=7 g=23 g_seq=32 -GoLabel dt=1 label_string=4 -GoBlock dt=34 reason_string=15 stack=5 -GoUnblock dt=14 g=23 g_seq=33 stack=0 -GoStart dt=4 g=23 g_seq=34 -GoLabel dt=2 label_string=2 -GoBlock dt=47 reason_string=15 stack=5 -GoUnblock dt=74 g=53 g_seq=19 stack=0 -GoStart dt=6 g=53 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=154 reason_string=15 stack=5 -GoStatus dt=91 g=56 m=18446744073709551615 gstatus=4 -GoUnblock dt=2 g=56 g_seq=1 stack=0 -GoStart dt=43 g=56 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=45 reason_string=15 stack=5 -GoUnblock dt=65 g=53 g_seq=23 stack=0 -GoStart dt=8 g=53 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=16 reason_string=15 stack=5 -ProcStop dt=2526 -ProcStart dt=208 p=30 p_seq=15 -GoUnblock dt=8 g=53 g_seq=37 stack=0 -GoStart dt=5 g=53 g_seq=38 -GoLabel dt=1 label_string=4 -GoBlock dt=694 reason_string=15 stack=5 -GoUnblock dt=14 g=53 g_seq=39 stack=0 -GoStart dt=4 g=53 g_seq=40 -GoLabel dt=3 label_string=2 -GoBlock dt=336 reason_string=15 stack=5 -GoUnblock dt=52 g=53 g_seq=41 stack=0 -GoStart dt=4 g=53 g_seq=42 -GoLabel dt=1 label_string=4 -GoUnblock dt=449 g=118 g_seq=1 stack=12 -GoBlock dt=17 reason_string=15 stack=5 -GoUnblock dt=65 g=24 g_seq=31 stack=0 -GoStart dt=6 g=24 g_seq=32 -GoLabel dt=2 label_string=4 -GoBlock dt=56 reason_string=15 stack=5 -GoUnblock dt=54 g=24 g_seq=33 stack=0 -GoStart dt=5 g=24 g_seq=34 -GoLabel dt=1 label_string=4 -GoBlock dt=489 reason_string=15 stack=5 -GoUnblock dt=9 g=24 g_seq=35 stack=0 -GoStart dt=4 g=24 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=1307 reason_string=15 stack=5 -ProcStop dt=84 -ProcStart dt=23944 p=15 p_seq=1 -GoStart dt=174 g=108 g_seq=3 -GCMarkAssistBegin dt=25 stack=3 -GoBlock dt=59 reason_string=10 stack=18 -ProcStop dt=71 -EventBatch gen=3 m=169421 time=28114950900230 size=330 -ProcStatus dt=1 p=33 pstatus=1 -GoStatus dt=5 g=81 m=169421 gstatus=2 -GCMarkAssistActive dt=3 g=81 -GoBlock dt=7 reason_string=13 stack=11 -GoStatus dt=1543 g=57 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=57 g_seq=1 stack=0 -GoStart dt=10 g=57 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=123 reason_string=15 stack=5 -GoStatus dt=1345 g=58 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=58 g_seq=1 stack=0 -GoStart dt=5 g=58 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=154 reason_string=15 stack=5 -GoUnblock dt=5938 g=54 g_seq=7 stack=0 -GoStart dt=7 g=54 g_seq=8 -GoLabel dt=1 label_string=4 -GoBlock dt=93 reason_string=15 stack=5 -GoStart dt=1331 g=97 g_seq=2 -GoStatus dt=26 g=93 m=18446744073709551615 gstatus=4 -GoUnblock dt=6 g=93 g_seq=1 stack=10 -GCMarkAssistBegin dt=18 stack=3 -GCMarkAssistEnd dt=1894 -HeapAlloc dt=57 heapalloc_value=191872448 -GCMarkAssistBegin dt=26 stack=3 -GoBlock dt=46 reason_string=13 stack=11 -GoUnblock dt=2442 g=52 g_seq=19 stack=0 -GoStart dt=14 g=52 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=767 reason_string=15 stack=5 -ProcStop dt=2248 -ProcStart dt=24 p=33 p_seq=1 -GoUnblock dt=8 g=72 g_seq=27 stack=0 -GoStart dt=7 g=72 g_seq=28 -GoLabel dt=1 label_string=4 -GoUnblock dt=172 g=119 g_seq=3 stack=12 -GoBlock dt=1629 reason_string=15 stack=5 -GoUnblock dt=71 g=28 g_seq=9 stack=0 -GoStart dt=7 g=28 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=276 reason_string=15 stack=5 -GoUnblock dt=72 g=28 g_seq=11 stack=0 -GoStart dt=4 g=28 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=2016 reason_string=15 stack=5 -GoUnblock dt=16 g=28 g_seq=13 stack=0 -GoStart dt=7 g=28 g_seq=14 -GoLabel dt=1 label_string=2 -GoBlock dt=6712 reason_string=15 stack=5 -ProcStop dt=63 -ProcStart dt=20808 p=14 p_seq=1 -GoStart dt=205 g=89 g_seq=7 -GCMarkAssistEnd dt=10 -HeapAlloc dt=64 heapalloc_value=196245968 -GoStop dt=6073 reason_string=16 stack=6 -GoStart dt=21 g=89 g_seq=8 -GCMarkAssistBegin dt=15 stack=3 -GoBlock dt=38 reason_string=10 stack=18 -ProcStop dt=129 -ProcStart dt=13557 p=11 p_seq=7 -GoStart dt=202 g=116 g_seq=12 -GCMarkAssistEnd dt=10 -GCSweepBegin dt=25 stack=28 -GCSweepEnd dt=12 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=114760576 -GCSweepBegin dt=70 stack=28 -GCSweepEnd dt=5 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=1 heapalloc_value=114785152 -GCSweepBegin dt=353 stack=27 -EventBatch gen=3 m=169420 time=28114950896337 size=112 -ProcStatus dt=2 p=17 pstatus=1 -GoStatus dt=1 g=84 m=169420 gstatus=2 -GCMarkAssistActive dt=1 g=84 -GCMarkAssistEnd dt=3 -HeapAlloc dt=20 heapalloc_value=190365120 -GCMarkAssistBegin dt=42 stack=3 -GoStop dt=861 reason_string=20 stack=9 -GoStart dt=142 g=126 g_seq=1 -GoBlock dt=2538 reason_string=13 stack=11 -GoUnblock dt=1653 g=30 g_seq=1 stack=0 -GoStart dt=7 g=30 g_seq=2 -GoLabel dt=2 label_string=4 -GoBlock dt=6064 reason_string=15 stack=5 -GoUnblock dt=1633 g=25 g_seq=11 stack=0 -GoStart dt=8 g=25 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=4927 reason_string=15 stack=5 -GoUnblock dt=3569 g=67 g_seq=11 stack=0 -GoStart dt=7 g=67 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=1289 reason_string=15 stack=5 -GoUnblock dt=73 g=67 g_seq=13 stack=0 -GoStart dt=4 g=67 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=46 reason_string=15 stack=5 -ProcStop dt=52 -EventBatch gen=3 m=169419 time=28114950898971 size=132 -ProcStatus dt=2 p=13 pstatus=1 -GoStatus dt=2 g=30 m=169419 gstatus=2 -GoBlock dt=7 reason_string=15 stack=5 -GoUnblock dt=2697 g=72 g_seq=1 stack=0 -GoStart dt=8 g=72 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=969 reason_string=15 stack=5 -GoStart dt=2978 g=95 g_seq=2 -GoStatus dt=32 g=88 m=18446744073709551615 gstatus=4 -GoUnblock dt=7 g=88 g_seq=1 stack=10 -GCMarkAssistBegin dt=18 stack=3 -GoStop dt=1258 reason_string=20 stack=9 -GoStart dt=17 g=88 g_seq=2 -GoStatus dt=27 g=113 m=18446744073709551615 gstatus=4 -GoUnblock dt=5 g=113 g_seq=1 stack=10 -GCMarkAssistBegin dt=12 stack=3 -GoStop dt=1797 reason_string=20 stack=9 -GoStart dt=18 g=88 g_seq=3 -GoStop dt=2883 reason_string=20 stack=9 -GoUnblock dt=14 g=70 g_seq=7 stack=0 -GoStart dt=5 g=70 g_seq=8 -GoLabel dt=3 label_string=2 -GoBlock dt=5294 reason_string=15 stack=5 -GoStart dt=14 g=123 g_seq=5 -GoBlock dt=18 reason_string=13 stack=11 -ProcStop dt=16 -EventBatch gen=3 m=169418 time=28114950895095 size=398 -ProcStatus dt=1 p=35 pstatus=2 -ProcStart dt=2 p=35 p_seq=1 -GoStart dt=38 g=87 g_seq=1 -HeapAlloc dt=103 heapalloc_value=190086592 -GCMarkAssistBegin dt=64 stack=3 -GCMarkAssistEnd dt=3228 -HeapAlloc dt=76 heapalloc_value=190995904 -GCMarkAssistBegin dt=149 stack=3 -GoBlock dt=3823 reason_string=13 stack=11 -GoStart dt=1406 g=82 g_seq=4 -GCMarkAssistEnd dt=12 -HeapAlloc dt=82 heapalloc_value=191618496 -GCMarkAssistBegin dt=75 stack=3 -GoStop dt=4342 reason_string=20 stack=9 -GoStart dt=17 g=82 g_seq=5 -GoStop dt=987 reason_string=20 stack=9 -GoStart dt=26 g=82 g_seq=6 -GoStop dt=3601 reason_string=20 stack=9 -GoUnblock dt=14 g=58 g_seq=17 stack=0 -GoStart dt=3 g=58 g_seq=18 -GoLabel dt=3 label_string=2 -GoBlock dt=1524 reason_string=15 stack=5 -GoUnblock dt=23 g=25 g_seq=15 stack=0 -GoStart dt=8 g=25 g_seq=16 -GoLabel dt=3 label_string=2 -GoBlock dt=7942 reason_string=15 stack=5 -ProcStop dt=2920 -ProcStart dt=69 p=31 p_seq=1 -GoUnblock dt=7 g=67 g_seq=25 stack=0 -GoStart dt=5 g=67 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=1990 reason_string=15 stack=5 -GoUnblock dt=110 g=67 g_seq=29 stack=0 -GoStart dt=6 g=67 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=39 reason_string=15 stack=5 -GoUnblock dt=64 g=52 g_seq=29 stack=0 -GoStart dt=1 g=52 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=40 reason_string=15 stack=5 -GoUnblock dt=72 g=29 g_seq=19 stack=0 -GoStart dt=7 g=29 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=65 reason_string=15 stack=5 -GoUnblock dt=1007 g=23 g_seq=43 stack=0 -GoStart dt=8 g=23 g_seq=44 -GoLabel dt=1 label_string=4 -GoBlock dt=1633 reason_string=15 stack=5 -GoUnblock dt=7 g=23 g_seq=45 stack=0 -GoStart dt=160 g=23 g_seq=46 -GoLabel dt=1 label_string=2 -GoBlock dt=16 reason_string=15 stack=5 -ProcStop dt=31 -ProcStart dt=8279 p=26 p_seq=2 -GoStart dt=216 g=103 g_seq=3 -GCMarkAssistBegin dt=19 stack=3 -GoBlock dt=41 reason_string=13 stack=11 -GoUnblock dt=11 g=25 g_seq=47 stack=0 -GoStart dt=3 g=25 g_seq=48 -GoLabel dt=1 label_string=2 -GoBlock dt=1274 reason_string=15 stack=5 -ProcStop dt=46 -ProcStart dt=1294 p=26 p_seq=3 -GoStart dt=164 g=127 g_seq=6 -GoStop dt=45 reason_string=20 stack=9 -GoStart dt=17 g=127 g_seq=7 -GCMarkAssistEnd dt=1921 -GoStop dt=49 reason_string=16 stack=17 -GoUnblock dt=17 g=22 g_seq=59 stack=0 -GoStart dt=8 g=22 g_seq=60 -GoLabel dt=1 label_string=2 -GoBlock dt=75 reason_string=15 stack=5 -GoStart dt=44 g=83 g_seq=7 -GCMarkAssistEnd dt=4 -GCMarkAssistBegin dt=50 stack=3 -GCMarkAssistEnd dt=27 -HeapAlloc dt=55 heapalloc_value=193551064 -GoStop dt=47 reason_string=16 stack=4 -GoStart dt=30 g=84 g_seq=7 -HeapAlloc dt=82 heapalloc_value=193772248 -HeapAlloc dt=291 heapalloc_value=194239192 -HeapAlloc dt=198 heapalloc_value=194493144 -HeapAlloc dt=7678 heapalloc_value=196524496 -GoStop dt=18 reason_string=16 stack=6 -ProcStop dt=218 -ProcStart dt=2082 p=26 p_seq=4 -ProcStop dt=68 -ProcStart dt=16818 p=14 p_seq=2 -GoStart dt=242 g=118 g_seq=7 -GCMarkAssistEnd dt=8 -GCSweepBegin dt=32 stack=28 -GCSweepEnd dt=11 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=3 heapalloc_value=114809728 -GCSweepBegin dt=279 stack=27 -EventBatch gen=3 m=169417 time=28114950894785 size=650 -ProcStatus dt=2 p=18 pstatus=1 -GoStatus dt=2 g=120 m=169417 gstatus=2 -GCMarkAssistActive dt=1 g=120 -GCMarkAssistEnd dt=2 -HeapAlloc dt=26 heapalloc_value=189767104 -GoStop dt=131 reason_string=16 stack=4 -GoStart dt=59 g=120 g_seq=1 -HeapAlloc dt=138 heapalloc_value=190045632 -GCMarkAssistBegin dt=31 stack=3 -GCMarkAssistEnd dt=3339 -HeapAlloc dt=63 heapalloc_value=190938560 -GCMarkAssistBegin dt=150 stack=3 -GoBlock dt=270 reason_string=13 stack=11 -GoUnblock dt=4058 g=57 g_seq=3 stack=0 -GoStart dt=7 g=57 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=902 reason_string=15 stack=5 -GoUnblock dt=4049 g=30 g_seq=3 stack=0 -GoStart dt=8 g=30 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=521 reason_string=15 stack=5 -GoUnblock dt=1581 g=57 g_seq=17 stack=0 -GoStart dt=11 g=57 g_seq=18 -GoLabel dt=3 label_string=4 -GoBlock dt=37 reason_string=15 stack=5 -GoUnblock dt=14 g=69 g_seq=11 stack=0 -GoStart dt=4 g=69 g_seq=12 -GoLabel dt=3 label_string=2 -GoBlock dt=543 reason_string=15 stack=5 -GoUnblock dt=10 g=69 g_seq=13 stack=0 -GoStart dt=6 g=69 g_seq=14 -GoLabel dt=1 label_string=2 -GoBlock dt=1813 reason_string=15 stack=5 -ProcStop dt=2875 -ProcStart dt=28 p=18 p_seq=1 -GoUnblock dt=1296 g=54 g_seq=23 stack=0 -GoStart dt=7 g=54 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=1516 reason_string=15 stack=5 -GoUnblock dt=7 g=54 g_seq=25 stack=0 -GoStart dt=5 g=54 g_seq=26 -GoLabel dt=1 label_string=2 -GoBlock dt=6851 reason_string=15 stack=5 -GoUnblock dt=71 g=72 g_seq=41 stack=0 -GoStart dt=5 g=72 g_seq=42 -GoLabel dt=1 label_string=4 -GoBlock dt=21 reason_string=15 stack=5 -GoUnblock dt=5 g=72 g_seq=43 stack=0 -GoStart dt=3 g=72 g_seq=44 -GoLabel dt=1 label_string=2 -GoBlock dt=62 reason_string=15 stack=5 -GoUnblock dt=19 g=72 g_seq=45 stack=0 -GoStart dt=4 g=72 g_seq=46 -GoLabel dt=1 label_string=2 -GoBlock dt=3727 reason_string=15 stack=5 -ProcStop dt=69 -ProcStart dt=21 p=18 p_seq=2 -GoUnblock dt=10 g=70 g_seq=15 stack=0 -GoStart dt=3 g=70 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=14 reason_string=15 stack=5 -GoUnblock dt=49 g=70 g_seq=17 stack=0 -GoStart dt=3 g=70 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=2314 reason_string=15 stack=5 -ProcStop dt=46 -ProcStart dt=1398 p=18 p_seq=3 -ProcStop dt=38 -ProcStart dt=4183 p=18 p_seq=4 -GoStart dt=183 g=96 g_seq=4 -GCMarkAssistEnd dt=9 -HeapAlloc dt=36 heapalloc_value=192305952 -GCMarkAssistBegin dt=28 stack=3 -GoBlock dt=1320 reason_string=13 stack=11 -GoUnblock dt=15 g=25 g_seq=45 stack=0 -GoStart dt=7 g=25 g_seq=46 -GoLabel dt=1 label_string=2 -GoBlock dt=600 reason_string=15 stack=5 -GoStart dt=13 g=89 g_seq=5 -GCMarkAssistBegin dt=11 stack=3 -GoBlock dt=112 reason_string=13 stack=11 -GoStart dt=14 g=121 g_seq=8 -GoStop dt=1488 reason_string=20 stack=9 -GoUnblock dt=16 g=25 g_seq=49 stack=0 -GoStart dt=7 g=25 g_seq=50 -GoLabel dt=2 label_string=2 -GoUnblock dt=1803 g=115 g_seq=6 stack=12 -GoUnblock dt=5 g=93 g_seq=6 stack=12 -GoUnblock dt=6 g=85 g_seq=5 stack=12 -GoUnblock dt=3 g=104 g_seq=1 stack=12 -GoUnblock dt=6 g=108 g_seq=1 stack=12 -GoUnblock dt=4 g=120 g_seq=4 stack=12 -GoUnblock dt=4 g=126 g_seq=5 stack=12 -GoUnblock dt=7 g=114 g_seq=3 stack=12 -GoUnblock dt=5 g=86 g_seq=4 stack=12 -GoUnblock dt=4 g=110 g_seq=3 stack=12 -GoBlock dt=14 reason_string=15 stack=5 -GoUnblock dt=7 g=25 g_seq=51 stack=0 -GoStart dt=1 g=25 g_seq=52 -GoLabel dt=1 label_string=2 -GoBlock dt=12 reason_string=15 stack=5 -GoStart dt=8 g=115 g_seq=7 -GCMarkAssistEnd dt=6 -HeapAlloc dt=32 heapalloc_value=192838360 -GCMarkAssistBegin dt=55 stack=3 -GoStop dt=1501 reason_string=20 stack=9 -GoUnblock dt=18 g=51 g_seq=51 stack=0 -GoStart dt=6 g=51 g_seq=52 -GoLabel dt=1 label_string=2 -GoUnblock dt=1117 g=105 g_seq=13 stack=12 -GoBlock dt=8 reason_string=15 stack=5 -GoStart dt=8 g=105 g_seq=14 -GCMarkAssistEnd dt=6 -GCMarkAssistBegin dt=27 stack=3 -GoBlock dt=13 reason_string=13 stack=11 -GoStart dt=7 g=86 g_seq=5 -GCMarkAssistEnd dt=6 -HeapAlloc dt=12 heapalloc_value=195148504 -GCMarkAssistBegin dt=65 stack=3 -HeapAlloc dt=22 heapalloc_value=195214040 -GoBlock dt=17 reason_string=10 stack=18 -GoStart dt=2881 g=124 g_seq=5 -HeapAlloc dt=35 heapalloc_value=195517144 -HeapAlloc dt=15 heapalloc_value=195566296 -HeapAlloc dt=61 heapalloc_value=195574224 -HeapAlloc dt=16 heapalloc_value=195631568 -GCMarkAssistBegin dt=23 stack=3 -GoBlock dt=34 reason_string=10 stack=18 -GoStart dt=14 g=90 g_seq=7 -GCMarkAssistEnd dt=5 -HeapAlloc dt=4 heapalloc_value=195697104 -GCMarkAssistBegin dt=58 stack=3 -GoBlock dt=15 reason_string=10 stack=18 -GoStart dt=10 g=96 g_seq=6 -GCMarkAssistEnd dt=3 -GCMarkAssistBegin dt=37 stack=3 -GoBlock dt=16 reason_string=13 stack=11 -GoStart dt=14 g=92 g_seq=5 -GCMarkAssistBegin dt=75 stack=3 -GoBlock dt=25 reason_string=10 stack=18 -GoStart dt=9 g=119 g_seq=6 -GCMarkAssistEnd dt=5 -HeapAlloc dt=20 heapalloc_value=195926480 -GCMarkAssistBegin dt=19 stack=3 -GoBlock dt=14 reason_string=10 stack=18 -GoStart dt=9 g=85 g_seq=6 -GCMarkAssistEnd dt=3 -HeapAlloc dt=38 heapalloc_value=195983824 -GoStop dt=5763 reason_string=16 stack=6 -GoStart dt=15 g=85 g_seq=7 -GCMarkAssistBegin dt=6 stack=3 -GoBlock dt=21 reason_string=10 stack=18 -ProcStop dt=46 -ProcStart dt=17429 p=15 p_seq=2 -GoStart dt=180 g=3 g_seq=2 -EventBatch gen=3 m=169416 time=28114950894874 size=516 -ProcStatus dt=2 p=26 pstatus=1 -GoStatus dt=1 g=98 m=169416 gstatus=2 -GCMarkAssistActive dt=1 g=98 -GCMarkAssistEnd dt=2 -HeapAlloc dt=136 heapalloc_value=190004672 -GoStop dt=29 reason_string=16 stack=4 -GoStart dt=29 g=86 g_seq=1 -GCMarkAssistBegin dt=75 stack=3 -GCMarkAssistEnd dt=8456 -HeapAlloc dt=48 heapalloc_value=191569344 -GoStop dt=32 reason_string=16 stack=4 -GoStart dt=19 g=86 g_seq=2 -GCMarkAssistBegin dt=73 stack=3 -GoStop dt=324 reason_string=20 stack=9 -GoStart dt=11 g=116 g_seq=3 -GoStop dt=270 reason_string=20 stack=9 -GoUnblock dt=14 g=51 g_seq=7 stack=0 -GoStart dt=4 g=51 g_seq=8 -GoLabel dt=3 label_string=2 -GoBlock dt=4139 reason_string=15 stack=5 -GoUnblock dt=9 g=51 g_seq=9 stack=0 -GoStart dt=6 g=51 g_seq=10 -GoLabel dt=1 label_string=2 -GoBlock dt=564 reason_string=15 stack=5 -GoUnblock dt=12 g=29 g_seq=7 stack=0 -GoStart dt=4 g=29 g_seq=8 -GoLabel dt=1 label_string=2 -GoBlock dt=13475 reason_string=15 stack=5 -ProcStop dt=61 -ProcStart dt=17 p=26 p_seq=1 -GoUnblock dt=6 g=53 g_seq=31 stack=0 -GoStart dt=3 g=53 g_seq=32 -GoLabel dt=1 label_string=4 -GoBlock dt=18 reason_string=15 stack=5 -GoUnblock dt=6 g=53 g_seq=33 stack=0 -GoStart dt=4 g=53 g_seq=34 -GoLabel dt=1 label_string=2 -GoBlock dt=2291 reason_string=15 stack=5 -GoUnblock dt=8 g=53 g_seq=35 stack=0 -GoStart dt=4 g=53 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=32 reason_string=15 stack=5 -GoUnblock dt=68 g=29 g_seq=9 stack=0 -GoStart dt=4 g=29 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=796 reason_string=15 stack=5 -GoUnblock dt=60 g=29 g_seq=11 stack=0 -GoStart dt=4 g=29 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=643 reason_string=15 stack=5 -GoUnblock dt=61 g=53 g_seq=43 stack=0 -GoStart dt=4 g=53 g_seq=44 -GoLabel dt=1 label_string=4 -GoBlock dt=3485 reason_string=15 stack=5 -GoUnblock dt=10 g=53 g_seq=45 stack=0 -GoStart dt=5 g=53 g_seq=46 -GoLabel dt=1 label_string=2 -GoBlock dt=14 reason_string=15 stack=5 -ProcStop dt=38 -ProcStart dt=9443 p=0 p_seq=3 -GoStart dt=226 g=115 g_seq=5 -GoBlock dt=168 reason_string=13 stack=11 -GoUnblock dt=11 g=30 g_seq=37 stack=0 -GoStart dt=6 g=30 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=67 reason_string=15 stack=5 -ProcStop dt=46 -ProcStart dt=1842 p=25 p_seq=6 -GoUnblock dt=12 g=29 g_seq=37 stack=0 -GoStart dt=5 g=29 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=2260 reason_string=15 stack=5 -GoUnblock dt=16 g=29 g_seq=39 stack=0 -GoStart dt=4 g=29 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=19 reason_string=15 stack=5 -GoStart dt=34 g=99 g_seq=4 -GCMarkAssistEnd dt=7 -HeapAlloc dt=55 heapalloc_value=193501912 -GoStop dt=29 reason_string=16 stack=4 -GoUnblock dt=18 g=29 g_seq=41 stack=0 -GoStart dt=7 g=29 g_seq=42 -GoLabel dt=1 label_string=2 -GoBlock dt=10 reason_string=15 stack=5 -GoUnblock dt=14 g=29 g_seq=43 stack=0 -GoStart dt=4 g=29 g_seq=44 -GoLabel dt=1 label_string=2 -GoBlock dt=40 reason_string=15 stack=5 -GoStart dt=16 g=111 g_seq=6 -GoBlock dt=37 reason_string=13 stack=11 -GoStart dt=13 g=125 g_seq=6 -GCMarkAssistBegin dt=13 stack=3 -GoBlock dt=34 reason_string=13 stack=11 -GoStart dt=23 g=115 g_seq=8 -GoBlock dt=61 reason_string=13 stack=11 -GoStart dt=27 g=120 g_seq=5 -GCMarkAssistEnd dt=12 -HeapAlloc dt=82 heapalloc_value=194067160 -GoStop dt=22 reason_string=16 stack=4 -GoStart dt=10 g=93 g_seq=7 -GCMarkAssistEnd dt=4 -HeapAlloc dt=663 heapalloc_value=194992856 -GCMarkAssistBegin dt=23 stack=3 -GoBlock dt=12 reason_string=13 stack=11 -GoStart dt=11 g=99 g_seq=7 -GCMarkAssistEnd dt=5 -HeapAlloc dt=4741 heapalloc_value=196180432 -GoStop dt=10 reason_string=16 stack=6 -GoStart dt=19 g=99 g_seq=8 -GCMarkAssistBegin dt=8 stack=3 -GoBlock dt=18 reason_string=10 stack=18 -GoStart dt=9 g=100 g_seq=4 -GCMarkAssistEnd dt=5 -HeapAlloc dt=101 heapalloc_value=196295120 -GoStop dt=6074 reason_string=16 stack=6 -GoStart dt=49 g=100 g_seq=5 -GCMarkAssistBegin dt=10 stack=3 -GoBlock dt=32 reason_string=13 stack=11 -ProcStop dt=67 -ProcStart dt=12947 p=10 p_seq=3 -GoStart dt=200 g=86 g_seq=7 -GoUnblock dt=38 g=124 g_seq=6 stack=30 -GCMarkAssistEnd dt=5 -HeapAlloc dt=90 heapalloc_value=113809792 -HeapAlloc dt=112 heapalloc_value=114160256 -GCSweepBegin dt=694 stack=31 -EventBatch gen=3 m=169415 time=28114950903030 size=633 -ProcStatus dt=1 p=28 pstatus=1 -GoStatus dt=3 g=91 m=169415 gstatus=2 -GCMarkAssistActive dt=1 g=91 -GCMarkAssistEnd dt=2 -HeapAlloc dt=29 heapalloc_value=191479232 -GCMarkAssistBegin dt=84 stack=3 -GoBlock dt=82 reason_string=13 stack=11 -GoStart dt=4920 g=113 g_seq=2 -GoStatus dt=31 g=123 m=18446744073709551615 gstatus=4 -GoUnblock dt=10 g=123 g_seq=1 stack=10 -GCMarkAssistBegin dt=14 stack=3 -GoStop dt=1855 reason_string=20 stack=9 -GoStart dt=15 g=113 g_seq=3 -GoStop dt=352 reason_string=20 stack=9 -GoStart dt=13 g=113 g_seq=4 -GoBlock dt=261 reason_string=13 stack=11 -GoUnblock dt=3404 g=52 g_seq=17 stack=0 -GoStart dt=7 g=52 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=1025 reason_string=15 stack=5 -GoUnblock dt=4703 g=67 g_seq=17 stack=0 -GoStart dt=8 g=67 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=1418 reason_string=15 stack=5 -GoUnblock dt=72 g=23 g_seq=29 stack=0 -GoStart dt=4 g=23 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=307 reason_string=15 stack=5 -GoUnblock dt=85 g=72 g_seq=33 stack=0 -GoStart dt=5 g=72 g_seq=34 -GoLabel dt=3 label_string=4 -GoBlock dt=30 reason_string=15 stack=5 -GoStatus dt=168 g=68 m=18446744073709551615 gstatus=4 -GoUnblock dt=2 g=68 g_seq=1 stack=0 -GoStart dt=64 g=68 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=55 reason_string=15 stack=5 -GoUnblock dt=10 g=68 g_seq=3 stack=0 -GoStart dt=3 g=68 g_seq=4 -GoLabel dt=2 label_string=2 -GoBlock dt=327 reason_string=15 stack=5 -ProcStop dt=80 -ProcStart dt=25 p=28 p_seq=1 -GoUnblock dt=7 g=30 g_seq=17 stack=0 -GoStart dt=4 g=30 g_seq=18 -GoLabel dt=3 label_string=4 -GoBlock dt=2630 reason_string=15 stack=5 -GoUnblock dt=28 g=72 g_seq=39 stack=0 -GoStart dt=12 g=72 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=21 reason_string=15 stack=5 -GoUnblock dt=77 g=30 g_seq=19 stack=0 -GoStart dt=10 g=30 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=3781 reason_string=15 stack=5 -GoUnblock dt=15 g=30 g_seq=21 stack=0 -GoStart dt=5 g=30 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=2537 reason_string=15 stack=5 -GoUnblock dt=55 g=30 g_seq=23 stack=0 -GoStart dt=5 g=30 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=478 reason_string=15 stack=5 -GoUnblock dt=8 g=30 g_seq=25 stack=0 -GoStart dt=4 g=30 g_seq=26 -GoLabel dt=1 label_string=2 -GoBlock dt=1039 reason_string=15 stack=5 -GoStart dt=26 g=14 g_seq=37 -GoBlock dt=1631 reason_string=15 stack=5 -GoUnblock dt=22 g=52 g_seq=43 stack=0 -GoStart dt=8 g=52 g_seq=44 -GoLabel dt=3 label_string=2 -GoBlock dt=21 reason_string=15 stack=5 -GoUnblock dt=9 g=52 g_seq=45 stack=0 -GoStart dt=3 g=52 g_seq=46 -GoLabel dt=1 label_string=2 -GoBlock dt=17 reason_string=15 stack=5 -GoUnblock dt=6 g=52 g_seq=47 stack=0 -GoStart dt=3 g=52 g_seq=48 -GoLabel dt=1 label_string=2 -GoUnblock dt=217 g=112 g_seq=3 stack=12 -GoBlock dt=298 reason_string=15 stack=5 -GoUnblock dt=8 g=52 g_seq=49 stack=0 -GoStart dt=3 g=52 g_seq=50 -GoLabel dt=1 label_string=2 -GoBlock dt=1919 reason_string=15 stack=5 -GoStart dt=16 g=121 g_seq=6 -GCMarkAssistEnd dt=6 -HeapAlloc dt=1354 heapalloc_value=192363224 -GoStop dt=25 reason_string=16 stack=4 -GoStart dt=16 g=121 g_seq=7 -GCMarkAssistBegin dt=74 stack=3 -GoStop dt=496 reason_string=20 stack=9 -GoUnblock dt=11 g=52 g_seq=55 stack=0 -GoStart dt=4 g=52 g_seq=56 -GoLabel dt=1 label_string=2 -GoUnblock dt=1666 g=94 g_seq=5 stack=12 -GoBlock dt=18 reason_string=15 stack=5 -GoUnblock dt=18 g=30 g_seq=41 stack=0 -GoStart dt=4 g=30 g_seq=42 -GoLabel dt=1 label_string=2 -GoUnblock dt=1362 g=84 g_seq=5 stack=12 -GoUnblock dt=6 g=125 g_seq=4 stack=12 -GoUnblock dt=5 g=118 g_seq=3 stack=12 -GoBlock dt=9 reason_string=15 stack=5 -GoUnblock dt=10 g=30 g_seq=43 stack=0 -GoStart dt=3 g=30 g_seq=44 -GoLabel dt=1 label_string=2 -GoBlock dt=9 reason_string=15 stack=5 -GoStart dt=6 g=84 g_seq=6 -GCMarkAssistEnd dt=5 -HeapAlloc dt=24 heapalloc_value=192748248 -GCMarkAssistBegin dt=83 stack=3 -GCMarkAssistEnd dt=1516 -HeapAlloc dt=28 heapalloc_value=193231576 -GoStop dt=27 reason_string=16 stack=6 -GoUnblock dt=14 g=22 g_seq=57 stack=0 -GoStart dt=3 g=22 g_seq=58 -GoLabel dt=1 label_string=2 -GoUnblock dt=16 g=81 g_seq=8 stack=12 -GoBlock dt=10 reason_string=15 stack=5 -GoStart dt=11 g=125 g_seq=5 -GCMarkAssistEnd dt=5 -HeapAlloc dt=16 heapalloc_value=193354456 -GoStop dt=95 reason_string=16 stack=6 -GoUnblock dt=34 g=22 g_seq=61 stack=0 -GoStart dt=1 g=22 g_seq=62 -GoLabel dt=1 label_string=2 -GoUnblock dt=1090 g=99 g_seq=6 stack=12 -GoBlock dt=10 reason_string=15 stack=5 -GoStart dt=8 g=81 g_seq=9 -GCMarkAssistEnd dt=5 -HeapAlloc dt=3528 heapalloc_value=195729872 -GoStop dt=10 reason_string=16 stack=6 -GoStart dt=17 g=81 g_seq=10 -GCMarkAssistBegin dt=9 stack=3 -GoBlock dt=34 reason_string=10 stack=18 -GoStart dt=20 g=121 g_seq=11 -GCMarkAssistEnd dt=4 -HeapAlloc dt=44 heapalloc_value=195852752 -GoStop dt=7425 reason_string=16 stack=6 -ProcStop dt=134 -ProcStart dt=14156 p=12 p_seq=2 -GoStart dt=200 g=84 g_seq=10 -GCMarkAssistEnd dt=6 -GCSweepBegin dt=35 stack=27 -EventBatch gen=3 m=169414 time=28114950903409 size=415 -ProcStatus dt=1 p=19 pstatus=1 -GoStatus dt=1 g=54 m=169414 gstatus=2 -GoBlock dt=7 reason_string=15 stack=5 -GoUnblock dt=2586 g=25 g_seq=5 stack=0 -GoStart dt=8 g=25 g_seq=6 -GoLabel dt=1 label_string=4 -GoBlock dt=2605 reason_string=15 stack=5 -GoUnblock dt=1216 g=71 g_seq=3 stack=0 -GoStart dt=7 g=71 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=672 reason_string=15 stack=5 -GoUnblock dt=7231 g=23 g_seq=15 stack=0 -GoStart dt=8 g=23 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=1212 reason_string=15 stack=5 -GoUnblock dt=11 g=23 g_seq=17 stack=0 -GoStart dt=7 g=23 g_seq=18 -GoLabel dt=3 label_string=2 -GoBlock dt=82 reason_string=15 stack=5 -GoUnblock dt=9 g=23 g_seq=19 stack=0 -GoStart dt=6 g=23 g_seq=20 -GoLabel dt=1 label_string=2 -GoBlock dt=162 reason_string=15 stack=5 -ProcStop dt=99 -ProcStart dt=3257 p=19 p_seq=1 -GoUnblock dt=13 g=68 g_seq=5 stack=0 -GoStart dt=10 g=68 g_seq=6 -GoLabel dt=1 label_string=2 -GoBlock dt=43 reason_string=15 stack=5 -GoUnblock dt=12 g=68 g_seq=7 stack=0 -GoStart dt=2 g=68 g_seq=8 -GoLabel dt=1 label_string=2 -GoBlock dt=133 reason_string=15 stack=5 -GoUnblock dt=23 g=58 g_seq=23 stack=0 -GoStart dt=6 g=58 g_seq=24 -GoLabel dt=1 label_string=2 -GoBlock dt=2822 reason_string=15 stack=5 -GoUnblock dt=11 g=69 g_seq=21 stack=0 -GoStart dt=7 g=69 g_seq=22 -GoLabel dt=2 label_string=2 -GoBlock dt=25 reason_string=15 stack=5 -GoUnblock dt=2937 g=58 g_seq=31 stack=0 -GoStart dt=6 g=58 g_seq=32 -GoLabel dt=1 label_string=4 -GoBlock dt=20 reason_string=15 stack=5 -ProcStop dt=60 -ProcStart dt=31 p=19 p_seq=2 -GoUnblock dt=9 g=56 g_seq=7 stack=0 -GoStart dt=6 g=56 g_seq=8 -GoLabel dt=3 label_string=4 -GoBlock dt=949 reason_string=15 stack=5 -ProcStop dt=41 -ProcStart dt=433 p=19 p_seq=3 -ProcStop dt=43 -ProcStart dt=9942 p=11 p_seq=4 -ProcStop dt=50 -ProcStart dt=2351 p=22 p_seq=6 -GoUnblock dt=15 g=30 g_seq=45 stack=0 -GoStart dt=205 g=30 g_seq=46 -GoLabel dt=1 label_string=2 -GoUnblock dt=145 g=113 g_seq=7 stack=12 -GoBlock dt=21 reason_string=15 stack=5 -GoStart dt=10 g=113 g_seq=8 -GCMarkAssistEnd dt=8 -HeapAlloc dt=48 heapalloc_value=192895704 -GCMarkAssistBegin dt=118 stack=3 -GCMarkAssistEnd dt=272 -HeapAlloc dt=20 heapalloc_value=192936664 -HeapAlloc dt=89 heapalloc_value=192953048 -HeapAlloc dt=41 heapalloc_value=192994008 -HeapAlloc dt=92 heapalloc_value=193059544 -HeapAlloc dt=102 heapalloc_value=193108696 -HeapAlloc dt=94 heapalloc_value=193133272 -HeapAlloc dt=42 heapalloc_value=193141464 -HeapAlloc dt=31 heapalloc_value=193207000 -GCMarkAssistBegin dt=142 stack=3 -GoBlock dt=114 reason_string=13 stack=11 -GoStart dt=179 g=109 g_seq=5 -GCMarkAssistEnd dt=8 -GCMarkAssistBegin dt=54 stack=3 -GCMarkAssistEnd dt=720 -HeapAlloc dt=23 heapalloc_value=194427608 -HeapAlloc dt=456 heapalloc_value=195001048 -GCMarkAssistBegin dt=18 stack=3 -GoBlock dt=22 reason_string=13 stack=11 -GoStart dt=23 g=113 g_seq=10 -GCMarkAssistEnd dt=3 -HeapAlloc dt=54 heapalloc_value=195099352 -GoStop dt=6390 reason_string=16 stack=6 -GoStart dt=23 g=113 g_seq=11 -GCMarkAssistBegin dt=6 stack=3 -GoBlock dt=21 reason_string=10 stack=18 -GoStart dt=33 g=101 g_seq=6 -GCMarkAssistEnd dt=6 -HeapAlloc dt=29 heapalloc_value=196409808 -GCMarkAssistBegin dt=22 stack=3 -GoBlock dt=52 reason_string=10 stack=18 -ProcStop dt=102 -EventBatch gen=3 m=169413 time=28114950897164 size=752 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=6 g=67 m=169413 gstatus=2 -GoBlock dt=11 reason_string=15 stack=5 -GoUnblock dt=18 g=25 g_seq=1 stack=0 -GoStart dt=7 g=25 g_seq=2 -GoLabel dt=1 label_string=2 -GoBlock dt=1315 reason_string=15 stack=5 -GoUnblock dt=11 g=25 g_seq=3 stack=0 -GoStart dt=6 g=25 g_seq=4 -GoLabel dt=1 label_string=2 -GoUnblock dt=4173 g=106 g_seq=1 stack=12 -GoBlock dt=1258 reason_string=15 stack=5 -GoUnblock dt=4804 g=30 g_seq=5 stack=0 -GoStart dt=7 g=30 g_seq=6 -GoLabel dt=1 label_string=4 -GoBlock dt=541 reason_string=15 stack=5 -GoUnblock dt=30 g=30 g_seq=7 stack=0 -GoStart dt=6 g=30 g_seq=8 -GoLabel dt=3 label_string=2 -GoBlock dt=3873 reason_string=15 stack=5 -GoUnblock dt=10 g=30 g_seq=9 stack=0 -GoStart dt=5 g=30 g_seq=10 -GoLabel dt=3 label_string=2 -GoBlock dt=3107 reason_string=15 stack=5 -GoUnblock dt=3672 g=14 g_seq=15 stack=0 -GoStart dt=6 g=14 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=442 reason_string=15 stack=5 -GoStart dt=32 g=83 g_seq=4 -GCMarkAssistEnd dt=7 -HeapAlloc dt=49 heapalloc_value=191962560 -GCMarkAssistBegin dt=108 stack=3 -GoStop dt=885 reason_string=20 stack=9 -GoStart dt=14 g=83 g_seq=5 -GoBlock dt=21 reason_string=13 stack=11 -ProcStop dt=93 -ProcStart dt=38 p=0 p_seq=1 -GoUnblock dt=7 g=53 g_seq=17 stack=0 -GoStart dt=2 g=53 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=31 reason_string=15 stack=5 -ProcStop dt=89 -ProcStart dt=45 p=11 p_seq=3 -GoUnblock dt=6 g=23 g_seq=35 stack=0 -GoStart dt=14 g=23 g_seq=36 -GoLabel dt=3 label_string=4 -GoBlock dt=2881 reason_string=15 stack=5 -GoUnblock dt=72 g=25 g_seq=17 stack=0 -GoStart dt=6 g=25 g_seq=18 -GoLabel dt=1 label_string=4 -GoBlock dt=19 reason_string=15 stack=5 -GoUnblock dt=58 g=25 g_seq=19 stack=0 -GoStart dt=3 g=25 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=13 reason_string=15 stack=5 -GoStart dt=16 g=94 g_seq=4 -GoBlock dt=356 reason_string=13 stack=11 -GoUnblock dt=80 g=52 g_seq=27 stack=0 -GoStart dt=9 g=52 g_seq=28 -GoLabel dt=1 label_string=4 -GoBlock dt=2325 reason_string=15 stack=5 -GoUnblock dt=57 g=67 g_seq=31 stack=0 -GoStart dt=4 g=67 g_seq=32 -GoLabel dt=1 label_string=4 -GoBlock dt=2043 reason_string=15 stack=5 -GoUnblock dt=9 g=67 g_seq=33 stack=0 -GoStart dt=171 g=67 g_seq=34 -GoLabel dt=5 label_string=2 -GoBlock dt=21 reason_string=15 stack=5 -ProcStop dt=60 -ProcStart dt=1735 p=25 p_seq=4 -GoUnblock dt=61 g=22 g_seq=39 stack=0 -GoStart dt=178 g=22 g_seq=40 -GoLabel dt=1 label_string=4 -GoBlock dt=66 reason_string=15 stack=5 -GoUnblock dt=8 g=22 g_seq=41 stack=0 -GoStart dt=4 g=22 g_seq=42 -GoLabel dt=1 label_string=2 -GoBlock dt=975 reason_string=15 stack=5 -ProcStop dt=1192 -ProcStart dt=347 p=25 p_seq=5 -GoUnblock dt=11 g=131 g_seq=6 stack=0 -GoStart dt=145 g=131 g_seq=7 -GoBlock dt=21 reason_string=15 stack=2 -GoUnblock dt=30 g=14 g_seq=38 stack=0 -GoStart dt=4 g=14 g_seq=39 -GoLabel dt=1 label_string=2 -GoBlock dt=65 reason_string=15 stack=5 -GoStart dt=26 g=130 g_seq=1 -ProcStatus dt=380 p=38 pstatus=2 -ProcStatus dt=4 p=39 pstatus=2 -ProcStatus dt=4 p=40 pstatus=2 -ProcStatus dt=3 p=41 pstatus=2 -ProcStatus dt=5 p=42 pstatus=2 -ProcStatus dt=5 p=43 pstatus=2 -ProcStatus dt=2 p=44 pstatus=2 -ProcStatus dt=3 p=45 pstatus=2 -ProcStatus dt=4 p=46 pstatus=2 -GoStop dt=1488 reason_string=16 stack=15 -GoUnblock dt=17 g=51 g_seq=45 stack=0 -GoStart dt=3 g=51 g_seq=46 -GoLabel dt=3 label_string=2 -GoBlock dt=1337 reason_string=15 stack=5 -GoStart dt=13 g=81 g_seq=7 -GCMarkAssistEnd dt=6 -GCMarkAssistBegin dt=31 stack=3 -GoBlock dt=20 reason_string=13 stack=11 -GoStart dt=5 g=130 g_seq=2 -HeapAlloc dt=98 heapalloc_value=192314072 -GoBlock dt=348 reason_string=12 stack=16 -GoStart dt=31 g=103 g_seq=2 -GCMarkAssistEnd dt=7 -HeapAlloc dt=53 heapalloc_value=192428760 -GoStop dt=173 reason_string=16 stack=6 -GoUnblock dt=18 g=71 g_seq=29 stack=0 -GoStart dt=4 g=71 g_seq=30 -GoLabel dt=3 label_string=2 -GoBlock dt=1289 reason_string=15 stack=5 -GoStart dt=17 g=126 g_seq=4 -GCMarkAssistBegin dt=125 stack=3 -GoBlock dt=23 reason_string=13 stack=11 -ProcStop dt=76 -ProcStart dt=2523 p=0 p_seq=4 -GoUnblock dt=16 g=30 g_seq=47 stack=0 -GoStart dt=196 g=30 g_seq=48 -GoLabel dt=2 label_string=2 -GoUnblock dt=1834 g=125 g_seq=7 stack=12 -GoBlock dt=17 reason_string=15 stack=5 -GoStart dt=14 g=125 g_seq=8 -GCMarkAssistEnd dt=5 -HeapAlloc dt=69 heapalloc_value=194566872 -GoStop dt=2253 reason_string=16 stack=6 -GoStart dt=2080 g=125 g_seq=9 -GCMarkAssistBegin dt=14 stack=3 -GoBlock dt=41 reason_string=10 stack=18 -GoStart dt=13 g=106 g_seq=8 -GCMarkAssistEnd dt=6 -HeapAlloc dt=53 heapalloc_value=196106704 -GoStop dt=6900 reason_string=16 stack=6 -GoStart dt=57 g=121 g_seq=12 -GCMarkAssistBegin dt=16 stack=3 -GoBlock dt=47 reason_string=10 stack=18 -ProcStop dt=83 -ProcStart dt=11930 p=7 p_seq=7 -GoStart dt=191 g=96 g_seq=8 -GCMarkAssistEnd dt=10 -HeapAlloc dt=59 heapalloc_value=109727392 -HeapAlloc dt=159 heapalloc_value=110336128 -HeapAlloc dt=109 heapalloc_value=110662528 -GCSweepBegin dt=144 stack=28 -GCSweepEnd dt=18 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=3 heapalloc_value=111288448 -GCSweepBegin dt=49 stack=28 -GCSweepEnd dt=14 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=5 heapalloc_value=111591296 -HeapAlloc dt=65 heapalloc_value=111888256 -HeapAlloc dt=228 heapalloc_value=112797056 -HeapAlloc dt=134 heapalloc_value=113322880 -HeapAlloc dt=83 heapalloc_value=113549696 -GCSweepBegin dt=35 stack=28 -GCSweepEnd dt=16 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=113842560 -HeapAlloc dt=75 heapalloc_value=114080128 -HeapAlloc dt=64 heapalloc_value=114307712 -HeapAlloc dt=134 heapalloc_value=114580608 -HeapAlloc dt=77 heapalloc_value=114670464 -GCSweepBegin dt=33 stack=28 -GCSweepEnd dt=6 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=3 heapalloc_value=114727808 -GCSweepBegin dt=90 stack=27 -EventBatch gen=3 m=169412 time=28114950898429 size=583 -ProcStatus dt=1 p=36 pstatus=2 -ProcStart dt=2 p=36 p_seq=1 -GoStart dt=401 g=83 g_seq=2 -GoBlock dt=1477 reason_string=13 stack=11 -GoStart dt=1208 g=81 g_seq=2 -GCMarkAssistEnd dt=9 -HeapAlloc dt=57 heapalloc_value=191348160 -GoStop dt=42 reason_string=16 stack=4 -GoStart dt=25 g=81 g_seq=3 -GCMarkAssistBegin dt=394 stack=3 -GoBlock dt=1177 reason_string=13 stack=11 -GoStart dt=28 g=106 g_seq=2 -GCMarkAssistEnd dt=10 -HeapAlloc dt=52 heapalloc_value=191503808 -GCMarkAssistBegin dt=52 stack=3 -GoStop dt=60 reason_string=20 stack=9 -GoUnblock dt=73 g=58 g_seq=3 stack=0 -GoStart dt=6 g=58 g_seq=4 -GoLabel dt=3 label_string=4 -GoBlock dt=2860 reason_string=15 stack=5 -GoUnblock dt=3777 g=24 g_seq=9 stack=0 -GoStart dt=6 g=24 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=41 reason_string=15 stack=5 -GoUnblock dt=1167 g=71 g_seq=9 stack=0 -GoStart dt=7 g=71 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=1396 reason_string=15 stack=5 -GoUnblock dt=1371 g=57 g_seq=23 stack=0 -GoStart dt=7 g=57 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=584 reason_string=15 stack=5 -GoUnblock dt=4657 g=23 g_seq=23 stack=0 -GoStart dt=7 g=23 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=40 reason_string=15 stack=5 -ProcStop dt=82 -ProcStart dt=1505 p=36 p_seq=2 -ProcStop dt=74 -ProcStart dt=19 p=36 p_seq=3 -GoUnblock dt=7 g=23 g_seq=27 stack=0 -GoStart dt=7 g=23 g_seq=28 -GoLabel dt=1 label_string=4 -GoBlock dt=122 reason_string=15 stack=5 -GoUnblock dt=58 g=52 g_seq=25 stack=0 -GoStart dt=6 g=52 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=4034 reason_string=15 stack=5 -GoUnblock dt=75 g=14 g_seq=19 stack=0 -GoStart dt=6 g=14 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=2059 reason_string=15 stack=5 -GoUnblock dt=63 g=14 g_seq=21 stack=0 -GoStart dt=4 g=14 g_seq=22 -GoLabel dt=1 label_string=4 -GoBlock dt=56 reason_string=15 stack=5 -ProcStop dt=49 -ProcStart dt=20 p=36 p_seq=4 -GoUnblock dt=6 g=67 g_seq=27 stack=0 -GoStart dt=2 g=67 g_seq=28 -GoLabel dt=1 label_string=4 -GoBlock dt=13 reason_string=15 stack=5 -ProcStop dt=1721 -ProcStart dt=20316 p=36 p_seq=5 -GoStart dt=197 g=94 g_seq=11 -GCMarkAssistEnd dt=7 -HeapAlloc dt=6672 heapalloc_value=196598224 -GoStop dt=15 reason_string=16 stack=6 -GoStart dt=54 g=106 g_seq=9 -GCMarkAssistBegin dt=16 stack=3 -GoBlock dt=32 reason_string=10 stack=18 -GoStart dt=41 g=103 g_seq=6 -GCMarkAssistBegin dt=15 stack=3 -GoBlock dt=84 reason_string=10 stack=18 -ProcStop dt=43 -ProcStart dt=10888 p=5 p_seq=1 -GoStart dt=189 g=120 g_seq=8 -GCMarkAssistEnd dt=7 -HeapAlloc dt=54 heapalloc_value=106433440 -HeapAlloc dt=94 heapalloc_value=106861728 -GCSweepBegin dt=92 stack=28 -GCSweepEnd dt=13 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=4 heapalloc_value=107301920 -HeapAlloc dt=65 heapalloc_value=107394848 -GCSweepBegin dt=32 stack=28 -GCSweepEnd dt=11 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=2 heapalloc_value=107616032 -HeapAlloc dt=60 heapalloc_value=107763488 -HeapAlloc dt=78 heapalloc_value=107953440 -HeapAlloc dt=65 heapalloc_value=108333088 -GCSweepBegin dt=38 stack=28 -GCSweepEnd dt=5 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=1 heapalloc_value=108423200 -GCSweepBegin dt=80 stack=28 -GCSweepEnd dt=9 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=108682656 -GCSweepBegin dt=61 stack=28 -GCSweepEnd dt=10 swept_value=8192 reclaimed_value=8192 -HeapAlloc dt=4 heapalloc_value=108816544 -HeapAlloc dt=32 heapalloc_value=108994080 -HeapAlloc dt=50 heapalloc_value=109290272 -HeapAlloc dt=112 heapalloc_value=109566240 -HeapAlloc dt=104 heapalloc_value=109973280 -GCSweepBegin dt=66 stack=29 -GCSweepEnd dt=17 swept_value=8192 reclaimed_value=0 -HeapAlloc dt=3 heapalloc_value=110183040 -HeapAlloc dt=86 heapalloc_value=110506880 -HeapAlloc dt=149 heapalloc_value=111151232 -HeapAlloc dt=24 heapalloc_value=111272064 -HeapAlloc dt=53 heapalloc_value=111368064 -HeapAlloc dt=68 heapalloc_value=111632256 -HeapAlloc dt=103 heapalloc_value=112078720 -GCSweepBegin dt=120 stack=28 -GCSweepEnd dt=7 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=3 heapalloc_value=112585472 -HeapAlloc dt=34 heapalloc_value=112616832 -HeapAlloc dt=39 heapalloc_value=112882304 -HeapAlloc dt=141 heapalloc_value=113391232 -HeapAlloc dt=80 heapalloc_value=113664384 -HeapAlloc dt=152 heapalloc_value=114242176 -HeapAlloc dt=104 heapalloc_value=114415616 -HeapAlloc dt=38 heapalloc_value=114527360 -HeapAlloc dt=28 heapalloc_value=114592896 -GCSweepBegin dt=227 stack=27 -EventBatch gen=3 m=169411 time=28114950895719 size=370 -ProcStatus dt=1 p=21 pstatus=1 -GoStatus dt=5 g=85 m=169411 gstatus=2 -GCMarkAssistActive dt=1 g=85 -GCMarkAssistEnd dt=3 -HeapAlloc dt=44 heapalloc_value=190299584 -GoStop dt=38 reason_string=16 stack=4 -GoStart dt=20 g=85 g_seq=1 -GCMarkAssistBegin dt=119 stack=3 -GoStop dt=4468 reason_string=20 stack=9 -GoStart dt=15 g=85 g_seq=2 -GoStop dt=1589 reason_string=20 stack=9 -GoStart dt=8 g=85 g_seq=3 -GCMarkAssistEnd dt=2892 -HeapAlloc dt=33 heapalloc_value=191733184 -GCMarkAssistBegin dt=98 stack=3 -GoStop dt=2309 reason_string=20 stack=9 -GoStart dt=10 g=95 g_seq=3 -GoBlock dt=153 reason_string=13 stack=11 -GoStart dt=5 g=85 g_seq=4 -GoBlock dt=18 reason_string=13 stack=11 -GoUnblock dt=3925 g=58 g_seq=13 stack=0 -GoStart dt=8 g=58 g_seq=14 -GoLabel dt=3 label_string=4 -GoBlock dt=106 reason_string=15 stack=5 -ProcStop dt=1275 -ProcStart dt=21 p=21 p_seq=1 -ProcStop dt=1335 -ProcStart dt=14 p=21 p_seq=2 -GoUnblock dt=1349 g=14 g_seq=9 stack=0 -GoStart dt=8 g=14 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=255 reason_string=15 stack=5 -GoUnblock dt=2226 g=70 g_seq=9 stack=0 -GoStart dt=8 g=70 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=398 reason_string=15 stack=5 -GoUnblock dt=8 g=70 g_seq=11 stack=0 -GoStart dt=6 g=70 g_seq=12 -GoLabel dt=1 label_string=2 -GoBlock dt=8210 reason_string=15 stack=5 -GoUnblock dt=12 g=70 g_seq=13 stack=0 -GoStart dt=5 g=70 g_seq=14 -GoLabel dt=2 label_string=2 -GoBlock dt=2354 reason_string=15 stack=5 -GoUnblock dt=93 g=72 g_seq=47 stack=0 -GoStart dt=9 g=72 g_seq=48 -GoLabel dt=1 label_string=4 -GoBlock dt=27 reason_string=15 stack=5 -GoUnblock dt=220 g=72 g_seq=49 stack=0 -GoStart dt=7 g=72 g_seq=50 -GoLabel dt=1 label_string=2 -GoBlock dt=20 reason_string=15 stack=5 -ProcStop dt=61 -ProcStart dt=16474 p=33 p_seq=2 -GoStart dt=3475 g=107 g_seq=4 -GCMarkAssistEnd dt=9 -HeapAlloc dt=52 heapalloc_value=196041168 -GoStop dt=5585 reason_string=16 stack=6 -GoStart dt=15 g=107 g_seq=5 -GCMarkAssistBegin dt=91 stack=3 -GoBlock dt=34 reason_string=10 stack=18 -ProcStop dt=55 -ProcStart dt=1514 p=33 p_seq=3 -ProcStop dt=41 -ProcStart dt=12390 p=8 p_seq=1 -GoStart dt=166 g=100 g_seq=7 -GCMarkAssistEnd dt=5 -HeapAlloc dt=51 heapalloc_value=111353984 -GCSweepBegin dt=133 stack=28 -GCSweepEnd dt=18 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=112029568 -HeapAlloc dt=68 heapalloc_value=112301312 -HeapAlloc dt=120 heapalloc_value=112739712 -HeapAlloc dt=116 heapalloc_value=113221760 -HeapAlloc dt=53 heapalloc_value=113380224 -HeapAlloc dt=115 heapalloc_value=113768832 -HeapAlloc dt=66 heapalloc_value=114026880 -HeapAlloc dt=127 heapalloc_value=114403328 -GCSweepBegin dt=47 stack=28 -GCSweepEnd dt=10 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=114503936 -HeapAlloc dt=67 heapalloc_value=114651264 -GCSweepBegin dt=299 stack=27 -EventBatch gen=3 m=169409 time=28114950894853 size=224 -ProcStatus dt=2 p=29 pstatus=1 -GoStatus dt=3 g=126 m=169409 gstatus=2 -HeapAlloc dt=3 heapalloc_value=189824448 -GCMarkAssistBegin dt=163 stack=3 -GoStop dt=1609 reason_string=20 stack=9 -GoStart dt=26 g=98 g_seq=2 -GCMarkAssistBegin dt=17 stack=3 -GCMarkAssistEnd dt=7751 -HeapAlloc dt=77 heapalloc_value=191675840 -GoStop dt=39 reason_string=16 stack=6 -GoStart dt=20 g=116 g_seq=4 -GoBlock dt=302 reason_string=13 stack=11 -GoUnblock dt=4886 g=51 g_seq=13 stack=0 -GoStart dt=8 g=51 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=2058 reason_string=15 stack=5 -GoUnblock dt=11 g=51 g_seq=15 stack=0 -GoStart dt=6 g=51 g_seq=16 -GoLabel dt=3 label_string=2 -GoBlock dt=2936 reason_string=15 stack=5 -GoUnblock dt=35 g=58 g_seq=21 stack=0 -GoStart dt=6 g=58 g_seq=22 -GoLabel dt=3 label_string=2 -GoBlock dt=7995 reason_string=15 stack=5 -GoUnblock dt=20 g=68 g_seq=9 stack=0 -GoStart dt=6 g=68 g_seq=10 -GoLabel dt=3 label_string=2 -GoBlock dt=92 reason_string=15 stack=5 -GoUnblock dt=8 g=68 g_seq=11 stack=0 -GoStart dt=1 g=68 g_seq=12 -GoLabel dt=1 label_string=2 -GoBlock dt=7039 reason_string=15 stack=5 -ProcStop dt=54 -ProcStart dt=14204 p=3 p_seq=1 -GoStart dt=213 g=94 g_seq=7 -GCMarkAssistBegin dt=29 stack=3 -GoBlock dt=62 reason_string=13 stack=11 -GoStart dt=20 g=124 g_seq=4 -GCMarkAssistEnd dt=6 -GCMarkAssistBegin dt=38 stack=3 -GCMarkAssistEnd dt=98 -HeapAlloc dt=118 heapalloc_value=193911512 -HeapAlloc dt=123 heapalloc_value=194116312 -HeapAlloc dt=352 heapalloc_value=194616024 -GoStop dt=3095 reason_string=16 stack=6 -GoStart dt=26 g=110 g_seq=4 -GCMarkAssistEnd dt=6 -HeapAlloc dt=30 heapalloc_value=195508952 -GoStop dt=4300 reason_string=16 stack=6 -GoStart dt=65 g=110 g_seq=5 -GCMarkAssistBegin dt=10 stack=3 -GoBlock dt=46 reason_string=10 stack=18 -ProcStop dt=124 -EventBatch gen=3 m=169408 time=28114950896863 size=856 -ProcStatus dt=1 p=22 pstatus=1 -GoStatus dt=2 g=105 m=169408 gstatus=2 -GCMarkAssistActive dt=1 g=105 -GCMarkAssistEnd dt=2 -HeapAlloc dt=22 heapalloc_value=190512576 -HeapAlloc dt=94 heapalloc_value=190537152 -GCMarkAssistBegin dt=18 stack=3 -GCMarkAssistEnd dt=1243 -HeapAlloc dt=34 heapalloc_value=190741952 -GCMarkAssistBegin dt=36 stack=3 -GCMarkAssistEnd dt=4423 -HeapAlloc dt=22 heapalloc_value=191413696 -GoStop dt=23 reason_string=16 stack=4 -GoStart dt=15 g=105 g_seq=1 -GCMarkAssistBegin dt=57 stack=3 -GoStop dt=662 reason_string=20 stack=9 -GoStart dt=12 g=105 g_seq=2 -GoStop dt=4139 reason_string=20 stack=9 -GoStart dt=11 g=105 g_seq=3 -GoStop dt=4306 reason_string=20 stack=9 -GoStart dt=15 g=105 g_seq=4 -GoBlock dt=21 reason_string=13 stack=11 -GoUnblock dt=2669 g=58 g_seq=19 stack=0 -GoStart dt=5 g=58 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=90 reason_string=15 stack=5 -GoUnblock dt=28 g=51 g_seq=17 stack=0 -GoStart dt=5 g=51 g_seq=18 -GoLabel dt=1 label_string=2 -GoBlock dt=5245 reason_string=15 stack=5 -GoUnblock dt=68 g=51 g_seq=19 stack=0 -GoStart dt=8 g=51 g_seq=20 -GoLabel dt=1 label_string=4 -GoBlock dt=14 reason_string=15 stack=5 -GoUnblock dt=6 g=51 g_seq=21 stack=0 -GoStart dt=1 g=51 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=7035 reason_string=15 stack=5 -GoUnblock dt=13 g=51 g_seq=23 stack=0 -GoStart dt=4 g=51 g_seq=24 -GoLabel dt=2 label_string=2 -GoUnblock dt=188 g=116 g_seq=5 stack=12 -GoBlock dt=65 reason_string=15 stack=5 -GoUnblock dt=9 g=51 g_seq=25 stack=0 -GoStart dt=2 g=51 g_seq=26 -GoLabel dt=1 label_string=2 -GoBlock dt=170 reason_string=15 stack=5 -GoUnblock dt=15 g=51 g_seq=27 stack=0 -GoStart dt=6 g=51 g_seq=28 -GoLabel dt=1 label_string=2 -GoBlock dt=33 reason_string=15 stack=5 -GoUnblock dt=7 g=51 g_seq=29 stack=0 -GoStart dt=6 g=51 g_seq=30 -GoLabel dt=1 label_string=2 -GoBlock dt=159 reason_string=15 stack=5 -GoUnblock dt=8 g=51 g_seq=31 stack=0 -GoStart dt=3 g=51 g_seq=32 -GoLabel dt=1 label_string=2 -GoBlock dt=124 reason_string=15 stack=5 -ProcStop dt=79 -ProcStart dt=18 p=22 p_seq=1 -GoUnblock dt=4 g=29 g_seq=21 stack=0 -GoStart dt=4 g=29 g_seq=22 -GoLabel dt=1 label_string=4 -GoBlock dt=28 reason_string=15 stack=5 -ProcStop dt=45 -ProcStart dt=12 p=22 p_seq=2 -GoUnblock dt=2 g=29 g_seq=23 stack=0 -GoStart dt=1 g=29 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=19 reason_string=15 stack=5 -GoUnblock dt=45 g=29 g_seq=25 stack=0 -GoStart dt=1 g=29 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=151 reason_string=15 stack=5 -GoUnblock dt=14 g=52 g_seq=35 stack=0 -GoStart dt=6 g=52 g_seq=36 -GoLabel dt=1 label_string=2 -GoBlock dt=13 reason_string=15 stack=5 -GoUnblock dt=4 g=52 g_seq=37 stack=0 -GoStart dt=3 g=52 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=127 reason_string=15 stack=5 -GoUnblock dt=7 g=52 g_seq=39 stack=0 -GoStart dt=1 g=52 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=11 reason_string=15 stack=5 -GoUnblock dt=6 g=52 g_seq=41 stack=0 -GoStart dt=2 g=52 g_seq=42 -GoLabel dt=1 label_string=2 -GoBlock dt=4594 reason_string=15 stack=5 -ProcStop dt=42 -ProcStart dt=1703 p=27 p_seq=42 -GoUnblock dt=17 g=22 g_seq=45 stack=0 -GoStart dt=283 g=22 g_seq=46 -GoLabel dt=2 label_string=2 -GoUnblock dt=103 g=96 g_seq=3 stack=12 -GoUnblock dt=95 g=121 g_seq=5 stack=12 -GoUnblock dt=5 g=126 g_seq=2 stack=12 -GoUnblock dt=529 g=115 g_seq=3 stack=12 -GoBlock dt=552 reason_string=15 stack=5 -GoUnblock dt=31 g=22 g_seq=47 stack=0 -GoStart dt=4 g=22 g_seq=48 -GoLabel dt=1 label_string=2 -GoUnblock dt=763 g=90 g_seq=3 stack=12 -GoBlock dt=39 reason_string=15 stack=5 -GoUnblock dt=12 g=22 g_seq=49 stack=0 -GoStart dt=4 g=22 g_seq=50 -GoLabel dt=1 label_string=2 -GoBlock dt=806 reason_string=15 stack=5 -GoStart dt=18 g=115 g_seq=4 -GCMarkAssistEnd dt=8 -HeapAlloc dt=834 heapalloc_value=192494296 -GCMarkAssistBegin dt=33 stack=3 -GoStop dt=622 reason_string=20 stack=9 -GoUnblock dt=15 g=14 g_seq=44 stack=0 -GoStart dt=5 g=14 g_seq=45 -GoLabel dt=1 label_string=2 -GoBlock dt=1768 reason_string=15 stack=5 -GoUnblock dt=11 g=14 g_seq=46 stack=0 -GoStart dt=4 g=14 g_seq=47 -GoLabel dt=1 label_string=2 -GoBlock dt=20 reason_string=15 stack=5 -GoUnblock dt=10 g=14 g_seq=48 stack=0 -GoStart dt=636 g=14 g_seq=49 -GoLabel dt=1 label_string=2 -GoBlock dt=55 reason_string=15 stack=5 -GoUnblock dt=18 g=14 g_seq=50 stack=0 -GoStart dt=3 g=14 g_seq=51 -GoLabel dt=1 label_string=2 -GoBlock dt=46 reason_string=15 stack=5 -GoUnblock dt=15 g=14 g_seq=52 stack=0 -GoStart dt=4 g=14 g_seq=53 -GoLabel dt=1 label_string=2 -GoBlock dt=26 reason_string=15 stack=5 -GoUnblock dt=29 g=70 g_seq=23 stack=0 -GoStart dt=5 g=70 g_seq=24 -GoLabel dt=1 label_string=2 -GoBlock dt=15 reason_string=15 stack=5 -GoStart dt=30 g=94 g_seq=6 -GCMarkAssistEnd dt=5 -HeapAlloc dt=37 heapalloc_value=192699096 -GoStop dt=34 reason_string=16 stack=6 -GoUnblock dt=9 g=70 g_seq=25 stack=0 -GoStart dt=3 g=70 g_seq=26 -GoLabel dt=1 label_string=2 -GoUnblock dt=190 g=98 g_seq=7 stack=12 -GoUnblock dt=6 g=91 g_seq=1 stack=12 -GoUnblock dt=7 g=123 g_seq=6 stack=12 -GoUnblock dt=5 g=100 g_seq=3 stack=12 -GoUnblock dt=3 g=102 g_seq=3 stack=12 -GoUnblock dt=3 g=103 g_seq=4 stack=12 -GoUnblock dt=5 g=117 g_seq=3 stack=12 -GoBlock dt=45 reason_string=15 stack=5 -GoUnblock dt=8 g=70 g_seq=27 stack=0 -GoStart dt=1 g=70 g_seq=28 -GoLabel dt=1 label_string=2 -GoUnblock dt=1939 g=111 g_seq=7 stack=12 -GoUnblock dt=10 g=101 g_seq=5 stack=12 -GoBlock dt=23 reason_string=15 stack=5 -GoStart dt=15 g=98 g_seq=8 -GCMarkAssistEnd dt=8 -HeapAlloc dt=57 heapalloc_value=193960664 -GCMarkAssistBegin dt=83 stack=3 -GoBlock dt=26 reason_string=13 stack=11 -GoStart dt=7 g=91 g_seq=2 -GCMarkAssistEnd dt=6 -HeapAlloc dt=47 heapalloc_value=194296536 -GCMarkAssistBegin dt=103 stack=3 -GoBlock dt=118 reason_string=13 stack=11 -GoStart dt=20 g=123 g_seq=7 -GCMarkAssistEnd dt=4 -HeapAlloc dt=448 heapalloc_value=195058392 -GoStop dt=6487 reason_string=16 stack=6 -GoStart dt=27 g=123 g_seq=8 -GCMarkAssistBegin dt=10 stack=3 -GoBlock dt=32 reason_string=10 stack=18 -ProcStop dt=78 -ProcStart dt=16845 p=9 p_seq=1 -GoStart dt=21 g=127 g_seq=10 -GCMarkAssistEnd dt=11 -GCSweepBegin dt=37 stack=28 -GCSweepEnd dt=17 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=7 heapalloc_value=110613376 -HeapAlloc dt=77 heapalloc_value=110956160 -HeapAlloc dt=127 heapalloc_value=111501184 -HeapAlloc dt=150 heapalloc_value=112133376 -HeapAlloc dt=103 heapalloc_value=112487168 -HeapAlloc dt=158 heapalloc_value=113166976 -GCSweepBegin dt=50 stack=28 -GCSweepEnd dt=32 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=6 heapalloc_value=113407616 -HeapAlloc dt=173 heapalloc_value=114067840 -HeapAlloc dt=153 heapalloc_value=114430208 -GCSweepBegin dt=35 stack=28 -GCSweepEnd dt=4 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=4 heapalloc_value=114551936 -GCSweepBegin dt=1034 stack=27 -EventBatch gen=3 m=169407 time=28114950901555 size=528 -ProcStatus dt=2 p=4 pstatus=1 -GoStatus dt=1 g=72 m=169407 gstatus=2 -GoBlock dt=7 reason_string=15 stack=5 -GoUnblock dt=1446 g=72 g_seq=3 stack=0 -GoStart dt=9 g=72 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=394 reason_string=15 stack=5 -GoStart dt=26 g=106 g_seq=3 -GoBlock dt=149 reason_string=13 stack=11 -GoUnblock dt=2557 g=72 g_seq=5 stack=0 -GoStart dt=8 g=72 g_seq=6 -GoLabel dt=1 label_string=4 -GoBlock dt=44 reason_string=15 stack=5 -GoUnblock dt=13 g=72 g_seq=7 stack=0 -GoStart dt=6 g=72 g_seq=8 -GoLabel dt=5 label_string=2 -GoBlock dt=1622 reason_string=15 stack=5 -GoUnblock dt=9 g=72 g_seq=9 stack=0 -GoStart dt=6 g=72 g_seq=10 -GoLabel dt=1 label_string=2 -GoUnblock dt=165 g=87 g_seq=2 stack=12 -GoBlock dt=854 reason_string=15 stack=5 -GoUnblock dt=9 g=72 g_seq=11 stack=0 -GoStart dt=4 g=72 g_seq=12 -GoLabel dt=1 label_string=2 -GoBlock dt=398 reason_string=15 stack=5 -GoUnblock dt=20 g=72 g_seq=13 stack=0 -GoStart dt=5 g=72 g_seq=14 -GoLabel dt=1 label_string=2 -GoBlock dt=1475 reason_string=15 stack=5 -GoStart dt=1158 g=93 g_seq=2 -GoStatus dt=24 g=94 m=18446744073709551615 gstatus=4 -GoUnblock dt=5 g=94 g_seq=1 stack=10 -GCMarkAssistBegin dt=19 stack=3 -GoBlock dt=235 reason_string=13 stack=11 -GoStart dt=9 g=94 g_seq=2 -GoStatus dt=18 g=100 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=100 g_seq=1 stack=10 -GCMarkAssistBegin dt=16 stack=3 -GoStop dt=7669 reason_string=20 stack=9 -GoStart dt=9 g=94 g_seq=3 -GoStop dt=5028 reason_string=20 stack=9 -GoUnblock dt=76 g=23 g_seq=39 stack=0 -GoStart dt=4 g=23 g_seq=40 -GoLabel dt=1 label_string=4 -GoBlock dt=464 reason_string=15 stack=5 -GoUnblock dt=67 g=23 g_seq=41 stack=0 -GoStart dt=151 g=23 g_seq=42 -GoLabel dt=2 label_string=4 -GoBlock dt=3280 reason_string=15 stack=5 -GoStart dt=35 g=113 g_seq=6 -GCMarkAssistEnd dt=7 -GCMarkAssistBegin dt=65 stack=3 -GoBlock dt=63 reason_string=13 stack=11 -ProcStop dt=162 -ProcStart dt=22113 p=24 p_seq=4 -GoStart dt=228 g=111 g_seq=8 -GCMarkAssistEnd dt=11 -HeapAlloc dt=64 heapalloc_value=196401616 -GoStop dt=6120 reason_string=16 stack=6 -GoStart dt=26 g=111 g_seq=9 -GCMarkAssistBegin dt=15 stack=3 -GoBlock dt=35 reason_string=10 stack=18 -ProcStop dt=128 -ProcStart dt=7783 p=1 p_seq=3 -GoStart dt=191 g=87 g_seq=8 -GCMarkAssistEnd dt=9 -GCSweepBegin dt=33 stack=28 -GCSweepEnd dt=16 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=4 heapalloc_value=103833248 -GCSweepBegin dt=56 stack=27 -GCSweepEnd dt=1508 swept_value=4194304 reclaimed_value=3072000 -HeapAlloc dt=33 heapalloc_value=105692064 -HeapAlloc dt=115 heapalloc_value=105976736 -HeapAlloc dt=44 heapalloc_value=106034080 -HeapAlloc dt=109 heapalloc_value=106332320 -HeapAlloc dt=95 heapalloc_value=106715424 -HeapAlloc dt=80 heapalloc_value=106958496 -HeapAlloc dt=97 heapalloc_value=107330592 -HeapAlloc dt=56 heapalloc_value=107460384 -HeapAlloc dt=117 heapalloc_value=107811360 -HeapAlloc dt=62 heapalloc_value=108141856 -HeapAlloc dt=115 heapalloc_value=108472352 -HeapAlloc dt=103 heapalloc_value=108710048 -GCSweepBegin dt=51 stack=28 -GCSweepEnd dt=11 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=4 heapalloc_value=108832928 -HeapAlloc dt=51 heapalloc_value=109134624 -HeapAlloc dt=100 heapalloc_value=109470496 -HeapAlloc dt=98 heapalloc_value=109831200 -HeapAlloc dt=69 heapalloc_value=110087968 -HeapAlloc dt=117 heapalloc_value=110388096 -HeapAlloc dt=150 heapalloc_value=111005312 -HeapAlloc dt=140 heapalloc_value=111509376 -HeapAlloc dt=55 heapalloc_value=111773568 -HeapAlloc dt=105 heapalloc_value=112162048 -GCSweepBegin dt=85 stack=28 -GCSweepEnd dt=8 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=112560896 -HeapAlloc dt=68 heapalloc_value=112816768 -HeapAlloc dt=47 heapalloc_value=112988800 -HeapAlloc dt=122 heapalloc_value=113464960 -HeapAlloc dt=150 heapalloc_value=114008448 -GCSweepBegin dt=885 stack=27 -EventBatch gen=3 m=169406 time=28114950897134 size=117 -ProcStatus dt=3 p=6 pstatus=1 -GoStatus dt=5 g=52 m=169406 gstatus=2 -GoBlock dt=14 reason_string=15 stack=5 -GoUnblock dt=16 g=52 g_seq=1 stack=0 -GoStart dt=5 g=52 g_seq=2 -GoLabel dt=1 label_string=2 -GoBlock dt=3752 reason_string=15 stack=5 -GoUnblock dt=21 g=52 g_seq=3 stack=0 -GoStart dt=7 g=52 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=4444 reason_string=15 stack=5 -GoUnblock dt=12 g=52 g_seq=5 stack=0 -GoStart dt=7 g=52 g_seq=6 -GoLabel dt=1 label_string=2 -GoBlock dt=5071 reason_string=15 stack=5 -GoUnblock dt=15 g=52 g_seq=7 stack=0 -GoStart dt=6 g=52 g_seq=8 -GoLabel dt=2 label_string=2 -GoBlock dt=2302 reason_string=15 stack=5 -GoUnblock dt=14 g=52 g_seq=9 stack=0 -GoStart dt=6 g=52 g_seq=10 -GoLabel dt=1 label_string=2 -GoBlock dt=32 reason_string=15 stack=5 -GoUnblock dt=9 g=52 g_seq=11 stack=0 -GoStart dt=6 g=52 g_seq=12 -GoLabel dt=1 label_string=2 -GoBlock dt=22 reason_string=15 stack=5 -ProcStop dt=35 -EventBatch gen=3 m=169405 time=28114950903578 size=119 -ProcStatus dt=2 p=15 pstatus=1 -GoStatus dt=4 g=53 m=169405 gstatus=2 -GoBlock dt=8 reason_string=15 stack=5 -GoUnblock dt=5238 g=25 g_seq=7 stack=0 -GoStart dt=7 g=25 g_seq=8 -GoLabel dt=1 label_string=4 -GoBlock dt=49 reason_string=15 stack=5 -GoUnblock dt=1111 g=58 g_seq=11 stack=0 -GoStart dt=6 g=58 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=158 reason_string=15 stack=5 -GoStart dt=3143 g=100 g_seq=2 -GoStatus dt=20 g=109 m=18446744073709551615 gstatus=4 -GoUnblock dt=7 g=109 g_seq=1 stack=10 -GCMarkAssistBegin dt=17 stack=3 -GoBlock dt=2307 reason_string=13 stack=11 -GoUnblock dt=2192 g=14 g_seq=13 stack=0 -GoStart dt=4 g=14 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=1366 reason_string=15 stack=5 -GoUnblock dt=68 g=23 g_seq=21 stack=0 -GoStart dt=4 g=23 g_seq=22 -GoLabel dt=1 label_string=4 -GoBlock dt=21 reason_string=15 stack=5 -ProcStop dt=3159 -EventBatch gen=3 m=169404 time=28114950896316 size=116 -ProcStatus dt=1 p=5 pstatus=1 -GoStatus dt=2 g=14 m=169404 gstatus=2 -GoBlock dt=5 reason_string=15 stack=5 -GoUnblock dt=1436 g=67 g_seq=3 stack=0 -GoStart dt=217 g=67 g_seq=4 -GoLabel dt=3 label_string=4 -GoBlock dt=1945 reason_string=15 stack=5 -GoStart dt=23 g=121 g_seq=3 -GoStop dt=570 reason_string=20 stack=9 -GoStart dt=14 g=121 g_seq=4 -GoBlock dt=1389 reason_string=13 stack=11 -GoUnblock dt=13 g=51 g_seq=3 stack=0 -GoStart dt=7 g=51 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=1439 reason_string=15 stack=5 -GoUnblock dt=17 g=14 g_seq=5 stack=0 -GoStart dt=5 g=14 g_seq=6 -GoLabel dt=2 label_string=2 -GoBlock dt=11474 reason_string=15 stack=5 -GoStart dt=4166 g=109 g_seq=3 -GoBlock dt=39 reason_string=13 stack=11 -GoStart dt=20 g=119 g_seq=4 -GCMarkAssistEnd dt=7 -HeapAlloc dt=68 heapalloc_value=191921600 -GCMarkAssistBegin dt=69 stack=3 -GoBlock dt=23 reason_string=13 stack=11 -ProcStop dt=59 -EventBatch gen=3 m=169402 time=28114950895074 size=135 -ProcStatus dt=2 p=9 pstatus=1 -GoStatus dt=2 g=25 m=169402 gstatus=2 -GoBlock dt=14 reason_string=15 stack=5 -GoStart dt=54 g=98 g_seq=1 -GCMarkAssistBegin dt=99 stack=3 -GCMarkAssistEnd dt=1187 -HeapAlloc dt=68 heapalloc_value=190463424 -GoStop dt=53 reason_string=16 stack=6 -GoStart dt=10 g=82 g_seq=1 -GCMarkAssistBegin dt=82 stack=3 -GoStop dt=2699 reason_string=20 stack=9 -GoStart dt=13 g=107 g_seq=2 -GCMarkAssistEnd dt=7 -GCMarkAssistBegin dt=49 stack=3 -GoBlock dt=852 reason_string=13 stack=11 -GoStart dt=29 g=90 g_seq=2 -GCMarkAssistEnd dt=3 -HeapAlloc dt=36 heapalloc_value=191233472 -GCMarkAssistBegin dt=825 stack=3 -GoBlock dt=392 reason_string=13 stack=11 -GoUnblock dt=21 g=67 g_seq=5 stack=0 -GoStart dt=5 g=67 g_seq=6 -GoLabel dt=1 label_string=2 -GoBlock dt=8638 reason_string=15 stack=5 -GoUnblock dt=9 g=67 g_seq=7 stack=0 -GoStart dt=4 g=67 g_seq=8 -GoLabel dt=1 label_string=2 -GoBlock dt=145 reason_string=15 stack=5 -GoUnblock dt=14 g=67 g_seq=9 stack=0 -GoStart dt=5 g=67 g_seq=10 -GoLabel dt=1 label_string=2 -GoBlock dt=7067 reason_string=15 stack=5 -ProcStop dt=23 -EventBatch gen=3 m=169401 time=28114950894770 size=505 -ProcStatus dt=1 p=8 pstatus=1 -GoStatus dt=1 g=130 m=169401 gstatus=2 -ProcsChange dt=124 procs_value=48 stack=1 -GCActive dt=3 gc_seq=4 -HeapAlloc dt=600 heapalloc_value=190152128 -HeapAlloc dt=16 heapalloc_value=190160320 -HeapAlloc dt=11095 heapalloc_value=191741376 -HeapAlloc dt=179 heapalloc_value=191749568 -HeapAlloc dt=14244 heapalloc_value=192011712 -HeapAlloc dt=292 heapalloc_value=192019904 -HeapAlloc dt=244 heapalloc_value=192028096 -HeapAlloc dt=3225 heapalloc_value=192036288 -HeapAlloc dt=39 heapalloc_value=192044192 -HeapAlloc dt=60 heapalloc_value=192052000 -HeapAlloc dt=462 heapalloc_value=192060192 -HeapAlloc dt=85 heapalloc_value=192068384 -HeapAlloc dt=341 heapalloc_value=192076576 -HeapAlloc dt=314 heapalloc_value=192142112 -GoStop dt=8367 reason_string=16 stack=14 -GoUnblock dt=274 g=30 g_seq=27 stack=0 -GoStart dt=6 g=30 g_seq=28 -GoLabel dt=1 label_string=2 -GoBlock dt=312 reason_string=15 stack=5 -GoUnblock dt=403 g=30 g_seq=29 stack=0 -GoStart dt=4 g=30 g_seq=30 -GoLabel dt=1 label_string=2 -GoBlock dt=773 reason_string=15 stack=5 -GoUnblock dt=7 g=30 g_seq=31 stack=0 -GoStart dt=3 g=30 g_seq=32 -GoLabel dt=1 label_string=2 -GoBlock dt=8 reason_string=15 stack=5 -GoStart dt=14 g=112 g_seq=4 -GCMarkAssistEnd dt=6 -HeapAlloc dt=45 heapalloc_value=192297760 -GCMarkAssistBegin dt=107 stack=3 -GoStop dt=897 reason_string=20 stack=9 -GoUnblock dt=15 g=70 g_seq=19 stack=0 -GoStart dt=5 g=70 g_seq=20 -GoLabel dt=1 label_string=2 -GoUnblock dt=1479 g=105 g_seq=5 stack=12 -GoBlock dt=2280 reason_string=15 stack=5 -GoUnblock dt=12 g=70 g_seq=21 stack=0 -GoStart dt=5 g=70 g_seq=22 -GoLabel dt=2 label_string=2 -GoBlock dt=1253 reason_string=15 stack=5 -GoUnblock dt=23 g=71 g_seq=35 stack=0 -GoStart dt=8 g=71 g_seq=36 -GoLabel dt=2 label_string=2 -GoBlock dt=26 reason_string=15 stack=5 -GoUnblock dt=6 g=71 g_seq=37 stack=0 -GoStart dt=3 g=71 g_seq=38 -GoLabel dt=1 label_string=2 -GoBlock dt=9 reason_string=15 stack=5 -GoUnblock dt=3 g=71 g_seq=39 stack=0 -GoStart dt=2 g=71 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=21 reason_string=15 stack=5 -GoUnblock dt=3 g=71 g_seq=41 stack=0 -GoStart dt=1 g=71 g_seq=42 -GoLabel dt=1 label_string=2 -GoUnblock dt=82 g=109 g_seq=4 stack=12 -GoUnblock dt=6 g=106 g_seq=4 stack=12 -GoUnblock dt=103 g=111 g_seq=4 stack=12 -GoUnblock dt=5 g=112 g_seq=6 stack=12 -GoUnblock dt=6 g=96 g_seq=5 stack=12 -GoUnblock dt=4 g=119 g_seq=5 stack=12 -GoUnblock dt=6 g=122 g_seq=1 stack=12 -GoUnblock dt=11 g=97 g_seq=5 stack=12 -GoUnblock dt=4 g=107 g_seq=3 stack=12 -GoUnblock dt=106 g=92 g_seq=3 stack=12 -GoUnblock dt=4 g=116 g_seq=9 stack=12 -GoUnblock dt=5 g=82 g_seq=8 stack=12 -GoBlock dt=9 reason_string=15 stack=5 -GoStart dt=12 g=111 g_seq=5 -GCMarkAssistEnd dt=5 -HeapAlloc dt=22 heapalloc_value=192797400 -GCMarkAssistBegin dt=75 stack=3 -GoStop dt=22 reason_string=20 stack=9 -GoUnblock dt=11 g=25 g_seq=53 stack=0 -GoStart dt=4 g=25 g_seq=54 -GoLabel dt=1 label_string=2 -GoUnblock dt=1354 g=95 g_seq=4 stack=12 -GoUnblock dt=9 g=90 g_seq=6 stack=12 -GoUnblock dt=6 g=113 g_seq=9 stack=12 -GoUnblock dt=3 g=89 g_seq=6 stack=12 -GoBlock dt=30 reason_string=15 stack=5 -GoStart dt=10 g=112 g_seq=7 -GCMarkAssistEnd dt=5 -GCMarkAssistBegin dt=28 stack=3 -GoBlock dt=587 reason_string=13 stack=11 -GoStart dt=6 g=116 g_seq=10 -GCMarkAssistEnd dt=5 -HeapAlloc dt=54 heapalloc_value=194337496 -GCMarkAssistBegin dt=51 stack=3 -GoBlock dt=21 reason_string=13 stack=11 -GoStart dt=8 g=82 g_seq=9 -GCMarkAssistEnd dt=6 -HeapAlloc dt=63 heapalloc_value=194525912 -GCMarkAssistBegin dt=51 stack=3 -GoBlock dt=45 reason_string=13 stack=11 -GoStart dt=22 g=95 g_seq=5 -GCMarkAssistEnd dt=6 -HeapAlloc dt=1508 heapalloc_value=195394264 -GoStop dt=6034 reason_string=16 stack=6 -GoStart dt=48 g=95 g_seq=6 -GCMarkAssistBegin dt=18 stack=3 -GoBlock dt=48 reason_string=10 stack=18 -ProcStop dt=85 -ProcStart dt=20619 p=17 p_seq=1 -GoStart dt=1507 g=130 g_seq=7 -EventBatch gen=3 m=169400 time=28114950894819 size=671 -ProcStatus dt=1 p=12 pstatus=1 -GoStatus dt=2 g=112 m=169400 gstatus=2 -GCMarkAssistBegin dt=120 stack=3 -GCMarkAssistEnd dt=3298 -HeapAlloc dt=41 heapalloc_value=190758336 -GCMarkAssistBegin dt=29 stack=3 -GoStop dt=2271 reason_string=20 stack=9 -GoStart dt=14 g=112 g_seq=1 -GoStop dt=569 reason_string=20 stack=9 -GoUnblock dt=2436 g=54 g_seq=1 stack=0 -GoStart dt=18 g=54 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=31 reason_string=15 stack=5 -GoUnblock dt=5090 g=57 g_seq=13 stack=0 -GoStart dt=6 g=57 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=734 reason_string=15 stack=5 -GoUnblock dt=4144 g=71 g_seq=15 stack=0 -GoStart dt=5 g=71 g_seq=16 -GoLabel dt=1 label_string=4 -GoUnblock dt=415 g=111 g_seq=2 stack=12 -GoBlock dt=5674 reason_string=15 stack=5 -GoUnblock dt=9 g=71 g_seq=17 stack=0 -GoStart dt=5 g=71 g_seq=18 -GoLabel dt=1 label_string=2 -GoUnblock dt=693 g=83 g_seq=3 stack=12 -GoBlock dt=4708 reason_string=15 stack=5 -GoUnblock dt=14 g=71 g_seq=19 stack=0 -GoStart dt=6 g=71 g_seq=20 -GoLabel dt=3 label_string=2 -GoBlock dt=1294 reason_string=15 stack=5 -GoUnblock dt=11 g=71 g_seq=21 stack=0 -GoStart dt=4 g=71 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=2434 reason_string=15 stack=5 -GoUnblock dt=8 g=71 g_seq=23 stack=0 -GoStart dt=3 g=71 g_seq=24 -GoLabel dt=1 label_string=2 -GoBlock dt=4227 reason_string=15 stack=5 -ProcStop dt=41 -ProcStart dt=3260 p=12 p_seq=1 -GoUnblock dt=16 g=30 g_seq=33 stack=0 -GoStart dt=143 g=30 g_seq=34 -GoLabel dt=1 label_string=2 -GoUnblock dt=553 g=89 g_seq=3 stack=12 -GoUnblock dt=971 g=127 g_seq=3 stack=12 -GoBlock dt=39 reason_string=15 stack=5 -GoStart dt=21 g=89 g_seq=4 -GCMarkAssistEnd dt=10 -HeapAlloc dt=1100 heapalloc_value=192510680 -GoStop dt=24 reason_string=16 stack=6 -GoUnblock dt=12 g=22 g_seq=51 stack=0 -GoStart dt=5 g=22 g_seq=52 -GoLabel dt=3 label_string=2 -GoBlock dt=1678 reason_string=15 stack=5 -GoUnblock dt=13 g=22 g_seq=53 stack=0 -GoStart dt=277 g=22 g_seq=54 -GoLabel dt=3 label_string=2 -GoBlock dt=960 reason_string=15 stack=5 -GoUnblock dt=8 g=22 g_seq=55 stack=0 -GoStart dt=4 g=22 g_seq=56 -GoLabel dt=1 label_string=2 -GoUnblock dt=583 g=99 g_seq=3 stack=12 -GoUnblock dt=5 g=83 g_seq=6 stack=12 -GoUnblock dt=5 g=124 g_seq=3 stack=12 -GoUnblock dt=6 g=105 g_seq=9 stack=12 -GoUnblock dt=1280 g=128 g_seq=3 stack=12 -GoUnblock dt=8 g=101 g_seq=3 stack=12 -GoBlock dt=7 reason_string=15 stack=5 -GoStart dt=11 g=128 g_seq=4 -GCMarkAssistEnd dt=7 -HeapAlloc dt=38 heapalloc_value=193297112 -GCMarkAssistBegin dt=118 stack=3 -GCMarkAssistEnd dt=44 -HeapAlloc dt=21 heapalloc_value=193403608 -GoStop dt=87 reason_string=16 stack=6 -GoStart dt=15 g=101 g_seq=4 -GCMarkAssistEnd dt=5 -HeapAlloc dt=58 heapalloc_value=193608408 -GCMarkAssistBegin dt=92 stack=3 -GoBlock dt=22 reason_string=13 stack=11 -GoStart dt=10 g=128 g_seq=5 -HeapAlloc dt=34 heapalloc_value=193829592 -HeapAlloc dt=166 heapalloc_value=194026200 -HeapAlloc dt=236 heapalloc_value=194419416 -HeapAlloc dt=885 heapalloc_value=195279576 -GoStop dt=6734 reason_string=16 stack=6 -GoUnblock dt=1628 g=130 g_seq=3 stack=0 -GoStart dt=136 g=130 g_seq=4 -HeapAlloc dt=62 heapalloc_value=196532688 -HeapAlloc dt=28 heapalloc_value=196540880 -HeapAlloc dt=22 heapalloc_value=196549072 -HeapAlloc dt=26 heapalloc_value=196557264 -HeapAlloc dt=38 heapalloc_value=196565456 -HeapAlloc dt=51 heapalloc_value=196573648 -GoStop dt=3032 reason_string=16 stack=19 -GoStart dt=10 g=117 g_seq=5 -GCMarkAssistBegin dt=16 stack=3 -GoBlock dt=51 reason_string=10 stack=18 -ProcStop dt=29 -ProcStart dt=9381 p=4 p_seq=2 -GoStart dt=190 g=105 g_seq=16 -GCMarkAssistEnd dt=4 -HeapAlloc dt=76 heapalloc_value=105214112 -HeapAlloc dt=103 heapalloc_value=105517216 -HeapAlloc dt=84 heapalloc_value=105642912 -HeapAlloc dt=85 heapalloc_value=105864096 -GCSweepBegin dt=188 stack=28 -GCSweepEnd dt=17 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=2 heapalloc_value=106376096 -HeapAlloc dt=43 heapalloc_value=106518816 -HeapAlloc dt=43 heapalloc_value=106756384 -HeapAlloc dt=82 heapalloc_value=106978976 -HeapAlloc dt=42 heapalloc_value=107091616 -GCSweepBegin dt=23 stack=28 -GCSweepEnd dt=8 swept_value=8192 reclaimed_value=8192 -HeapAlloc dt=3 heapalloc_value=107310112 -HeapAlloc dt=35 heapalloc_value=107372960 -HeapAlloc dt=65 heapalloc_value=107583264 -HeapAlloc dt=141 heapalloc_value=108018976 -HeapAlloc dt=161 heapalloc_value=108567968 -GCSweepBegin dt=85 stack=28 -GCSweepEnd dt=9 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=4 heapalloc_value=108808352 -HeapAlloc dt=90 heapalloc_value=109241120 -HeapAlloc dt=139 heapalloc_value=109623584 -HeapAlloc dt=162 heapalloc_value=110175008 -HeapAlloc dt=164 heapalloc_value=110769024 -HeapAlloc dt=246 heapalloc_value=111705984 -HeapAlloc dt=187 heapalloc_value=112446208 -HeapAlloc dt=161 heapalloc_value=113148544 -HeapAlloc dt=295 heapalloc_value=114145664 -GCSweepBegin dt=159 stack=28 -GCSweepEnd dt=5 swept_value=8192 reclaimed_value=8192 -HeapAlloc dt=7 heapalloc_value=114588800 -GCSweepBegin dt=48 stack=27 -EventBatch gen=3 m=169398 time=28114950899192 size=165 -ProcStatus dt=1 p=37 pstatus=2 -ProcStart dt=2 p=37 p_seq=1 -GoStatus dt=3261 g=29 m=18446744073709551615 gstatus=4 -GoUnblock dt=6 g=29 g_seq=1 stack=0 -GoStart dt=10 g=29 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=1840 reason_string=15 stack=5 -GoStart dt=16 g=86 g_seq=3 -GoBlock dt=1090 reason_string=13 stack=11 -ProcStop dt=1389 -ProcStart dt=16 p=37 p_seq=2 -GoStart dt=1537 g=84 g_seq=4 -GCMarkAssistEnd dt=7 -HeapAlloc dt=55 heapalloc_value=191847872 -GCMarkAssistBegin dt=85 stack=3 -GoBlock dt=249 reason_string=13 stack=11 -GoUnblock dt=1134 g=58 g_seq=9 stack=0 -GoStart dt=7 g=58 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=27 reason_string=15 stack=5 -GoUnblock dt=2190 g=53 g_seq=9 stack=0 -GoStart dt=8 g=53 g_seq=10 -GoLabel dt=1 label_string=4 -GoBlock dt=21 reason_string=15 stack=5 -GoUnblock dt=2156 g=25 g_seq=13 stack=0 -GoStart dt=4 g=25 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=20 reason_string=15 stack=5 -GoUnblock dt=1089 g=14 g_seq=7 stack=0 -GoStart dt=4 g=14 g_seq=8 -GoLabel dt=1 label_string=4 -GoBlock dt=107 reason_string=15 stack=5 -GoUnblock dt=1081 g=24 g_seq=15 stack=0 -GoStart dt=6 g=24 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=19 reason_string=15 stack=5 -ProcStop dt=1075 -EventBatch gen=3 m=169397 time=28114950897533 size=734 -ProcStatus dt=1 p=25 pstatus=1 -GoStatus dt=2 g=118 m=169397 gstatus=2 -GCMarkAssistActive dt=1 g=118 -GCMarkAssistEnd dt=2 -HeapAlloc dt=37 heapalloc_value=190684608 -GCMarkAssistBegin dt=79 stack=3 -GoBlock dt=1327 reason_string=13 stack=11 -ProcStop dt=4643 -ProcStart dt=23 p=25 p_seq=1 -GoUnblock dt=20 g=53 g_seq=1 stack=0 -GoStart dt=9 g=53 g_seq=2 -GoLabel dt=1 label_string=2 -GoBlock dt=2529 reason_string=15 stack=5 -GoStart dt=3244 g=123 g_seq=2 -GoStatus dt=30 g=97 m=18446744073709551615 gstatus=4 -GoUnblock dt=13 g=97 g_seq=1 stack=10 -GCMarkAssistBegin dt=20 stack=3 -GoStop dt=1976 reason_string=20 stack=9 -GoStart dt=15 g=123 g_seq=3 -GoStop dt=2654 reason_string=20 stack=9 -GoStart dt=12 g=123 g_seq=4 -GoStop dt=2704 reason_string=20 stack=9 -GoUnblock dt=9 g=24 g_seq=17 stack=0 -GoStart dt=4 g=24 g_seq=18 -GoLabel dt=1 label_string=2 -GoBlock dt=4029 reason_string=15 stack=5 -GoUnblock dt=14 g=24 g_seq=19 stack=0 -GoStart dt=4 g=24 g_seq=20 -GoLabel dt=1 label_string=2 -GoBlock dt=534 reason_string=15 stack=5 -GoUnblock dt=4 g=24 g_seq=21 stack=0 -GoStart dt=4 g=24 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=250 reason_string=15 stack=5 -GoUnblock dt=12 g=24 g_seq=23 stack=0 -GoStart dt=4 g=24 g_seq=24 -GoLabel dt=1 label_string=2 -GoBlock dt=22 reason_string=15 stack=5 -ProcStop dt=71 -ProcStart dt=244 p=25 p_seq=2 -ProcStop dt=54 -ProcStart dt=25 p=25 p_seq=3 -GoUnblock dt=8 g=53 g_seq=21 stack=0 -GoStart dt=7 g=53 g_seq=22 -GoLabel dt=1 label_string=4 -GoBlock dt=86 reason_string=15 stack=5 -GoUnblock dt=59 g=56 g_seq=3 stack=0 -GoStart dt=4 g=56 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=6219 reason_string=15 stack=5 -GoUnblock dt=52 g=56 g_seq=5 stack=0 -GoStart dt=4 g=56 g_seq=6 -GoLabel dt=1 label_string=4 -GoBlock dt=98 reason_string=15 stack=5 -GoUnblock dt=61 g=14 g_seq=27 stack=0 -GoStart dt=4 g=14 g_seq=28 -GoLabel dt=1 label_string=4 -GoBlock dt=32 reason_string=15 stack=5 -GoUnblock dt=13 g=14 g_seq=29 stack=0 -GoStart dt=5 g=14 g_seq=30 -GoLabel dt=1 label_string=2 -GoBlock dt=2423 reason_string=15 stack=5 -ProcStop dt=36 -ProcStart dt=7135 p=31 p_seq=2 -GoStart dt=228 g=127 g_seq=4 -GCMarkAssistEnd dt=9 -HeapAlloc dt=2440 heapalloc_value=192666328 -GoStop dt=28 reason_string=16 stack=4 -GoUnblock dt=19 g=52 g_seq=57 stack=0 -GoStart dt=6 g=52 g_seq=58 -GoLabel dt=1 label_string=2 -GoBlock dt=1072 reason_string=15 stack=5 -GoUnblock dt=16 g=52 g_seq=59 stack=0 -GoStart dt=6 g=52 g_seq=60 -GoLabel dt=1 label_string=2 -GoBlock dt=19 reason_string=15 stack=5 -GoUnblock dt=17 g=54 g_seq=39 stack=0 -GoStart dt=4 g=54 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=2352 reason_string=15 stack=5 -GoStart dt=22 g=127 g_seq=8 -GCMarkAssistBegin dt=127 stack=3 -GoBlock dt=42 reason_string=13 stack=11 -GoStart dt=766 g=122 g_seq=2 -GCMarkAssistEnd dt=2 -HeapAlloc dt=19 heapalloc_value=194902744 -GCMarkAssistBegin dt=66 stack=3 -STWBegin dt=12586 kind_string=21 stack=21 -GoUnblock dt=699 g=91 g_seq=3 stack=22 -GoUnblock dt=5 g=127 g_seq=9 stack=22 -GoUnblock dt=3 g=112 g_seq=8 stack=22 -GoUnblock dt=4 g=82 g_seq=10 stack=22 -GoUnblock dt=3 g=116 g_seq=11 stack=22 -GoUnblock dt=3 g=93 g_seq=8 stack=22 -GoUnblock dt=4 g=109 g_seq=6 stack=22 -GoUnblock dt=5 g=115 g_seq=9 stack=22 -GoUnblock dt=7 g=120 g_seq=7 stack=22 -GoUnblock dt=7 g=105 g_seq=15 stack=22 -GoUnblock dt=6 g=96 g_seq=7 stack=22 -GoUnblock dt=3 g=118 g_seq=6 stack=22 -GoUnblock dt=4 g=87 g_seq=7 stack=22 -GoUnblock dt=4 g=84 g_seq=9 stack=22 -GoUnblock dt=6 g=100 g_seq=6 stack=22 -GoUnblock dt=29 g=86 g_seq=6 stack=23 -HeapAlloc dt=53 heapalloc_value=103773088 -GoStatus dt=10 g=3 m=18446744073709551615 gstatus=4 -GoUnblock dt=7 g=3 g_seq=1 stack=24 -GCEnd dt=3 gc_seq=5 -HeapGoal dt=6 heapgoal_value=207987496 -ProcsChange dt=45 procs_value=48 stack=25 -STWEnd dt=399 -GoUnblock dt=5992 g=130 g_seq=6 stack=26 -GCMarkAssistEnd dt=9 -HeapAlloc dt=11 heapalloc_value=103816864 -GCSweepBegin dt=79 stack=27 -GCSweepEnd dt=1631 swept_value=8388608 reclaimed_value=3260416 -HeapAlloc dt=14 heapalloc_value=104810272 -HeapAlloc dt=104 heapalloc_value=105001504 -HeapAlloc dt=107 heapalloc_value=105164960 -HeapAlloc dt=55 heapalloc_value=105308320 -HeapAlloc dt=200 heapalloc_value=105798560 -HeapAlloc dt=119 heapalloc_value=106091424 -HeapAlloc dt=118 heapalloc_value=106359712 -HeapAlloc dt=47 heapalloc_value=106488096 -HeapAlloc dt=44 heapalloc_value=106763424 -HeapAlloc dt=26 heapalloc_value=106820768 -HeapAlloc dt=106 heapalloc_value=107277344 -HeapAlloc dt=131 heapalloc_value=107656992 -HeapAlloc dt=71 heapalloc_value=107790880 -GCSweepBegin dt=42 stack=28 -GCSweepEnd dt=6 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=3 heapalloc_value=107860512 -HeapAlloc dt=71 heapalloc_value=108305696 -HeapAlloc dt=113 heapalloc_value=108608928 -HeapAlloc dt=129 heapalloc_value=108890272 -HeapAlloc dt=147 heapalloc_value=109508896 -HeapAlloc dt=88 heapalloc_value=109776544 -HeapAlloc dt=140 heapalloc_value=110286976 -HeapAlloc dt=151 heapalloc_value=110900096 -HeapAlloc dt=152 heapalloc_value=111433600 -HeapAlloc dt=136 heapalloc_value=111931264 -HeapAlloc dt=67 heapalloc_value=112248064 -HeapAlloc dt=209 heapalloc_value=113046144 -HeapAlloc dt=213 heapalloc_value=113949056 -HeapAlloc dt=236 heapalloc_value=114471168 -HeapAlloc dt=90 heapalloc_value=114663552 -GCSweepBegin dt=45 stack=28 -GCSweepEnd dt=10 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=3 heapalloc_value=114703232 -GCSweepBegin dt=54 stack=27 -EventBatch gen=3 m=169396 time=28114950894859 size=148 -ProcStatus dt=2 p=1 pstatus=1 -GoStatus dt=4 g=86 m=169396 gstatus=2 -GCMarkAssistActive dt=2 g=86 -GCMarkAssistEnd dt=2 -HeapAlloc dt=42 heapalloc_value=189889984 -GoStop dt=32 reason_string=16 stack=4 -GoUnblock dt=117 g=69 g_seq=1 stack=0 -GoStart dt=6 g=69 g_seq=2 -GoLabel dt=1 label_string=2 -GoBlock dt=2672 reason_string=15 stack=5 -GoStart dt=16 g=84 g_seq=1 -GoStop dt=2565 reason_string=20 stack=9 -GoStart dt=17 g=84 g_seq=2 -GoBlock dt=886 reason_string=13 stack=11 -ProcStop dt=2581 -ProcStart dt=17 p=1 p_seq=1 -ProcStop dt=4400 -ProcStart dt=16 p=1 p_seq=2 -GoStart dt=24 g=87 g_seq=3 -GCMarkAssistEnd dt=8 -HeapAlloc dt=70 heapalloc_value=191782336 -GCMarkAssistBegin dt=85 stack=3 -GoBlock dt=1055 reason_string=13 stack=11 -GoUnblock dt=20 g=54 g_seq=9 stack=0 -GoStart dt=7 g=54 g_seq=10 -GoLabel dt=3 label_string=2 -GoBlock dt=230 reason_string=15 stack=5 -GoUnblock dt=12 g=54 g_seq=11 stack=0 -GoStart dt=6 g=54 g_seq=12 -GoLabel dt=1 label_string=2 -GoBlock dt=1754 reason_string=15 stack=5 -GoUnblock dt=12 g=54 g_seq=13 stack=0 -GoStart dt=8 g=54 g_seq=14 -GoLabel dt=3 label_string=2 -GoBlock dt=1379 reason_string=15 stack=5 -ProcStop dt=15 -EventBatch gen=3 m=169395 time=28114950898507 size=532 -ProcStatus dt=2 p=14 pstatus=1 -GoStatus dt=2 g=103 m=169395 gstatus=2 -GCMarkAssistActive dt=1 g=103 -GCMarkAssistEnd dt=3 -HeapAlloc dt=40 heapalloc_value=190873024 -HeapAlloc dt=75 heapalloc_value=191036864 -GCMarkAssistBegin dt=65 stack=3 -GoBlock dt=6142 reason_string=13 stack=11 -GoStart dt=19 g=98 g_seq=3 -GCMarkAssistBegin dt=20 stack=3 -GoStop dt=1738 reason_string=20 stack=9 -GoStart dt=16 g=98 g_seq=4 -GoBlock dt=2102 reason_string=13 stack=11 -GoUnblock dt=2317 g=71 g_seq=5 stack=0 -GoStart dt=5 g=71 g_seq=6 -GoLabel dt=2 label_string=4 -GoBlock dt=128 reason_string=15 stack=5 -GoUnblock dt=2283 g=71 g_seq=13 stack=0 -GoStart dt=7 g=71 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=97 reason_string=15 stack=5 -GoUnblock dt=1168 g=24 g_seq=13 stack=0 -GoStart dt=7 g=24 g_seq=14 -GoLabel dt=1 label_string=4 -GoBlock dt=1399 reason_string=15 stack=5 -GoUnblock dt=3752 g=23 g_seq=25 stack=0 -GoStart dt=6 g=23 g_seq=26 -GoLabel dt=3 label_string=4 -GoBlock dt=1167 reason_string=15 stack=5 -GoUnblock dt=99 g=52 g_seq=23 stack=0 -GoStart dt=35 g=52 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=47 reason_string=15 stack=5 -GoUnblock dt=81 g=67 g_seq=19 stack=0 -GoStart dt=8 g=67 g_seq=20 -GoLabel dt=3 label_string=4 -GoBlock dt=3975 reason_string=15 stack=5 -GoUnblock dt=18 g=67 g_seq=21 stack=0 -GoStart dt=6 g=67 g_seq=22 -GoLabel dt=1 label_string=2 -GoBlock dt=80 reason_string=15 stack=5 -GoUnblock dt=18 g=67 g_seq=23 stack=0 -GoStart dt=6 g=67 g_seq=24 -GoLabel dt=1 label_string=2 -GoBlock dt=22 reason_string=15 stack=5 -GoUnblock dt=3174 g=14 g_seq=23 stack=0 -GoStart dt=7 g=14 g_seq=24 -GoLabel dt=1 label_string=4 -GoBlock dt=22 reason_string=15 stack=5 -GoUnblock dt=9 g=14 g_seq=25 stack=0 -GoStart dt=2 g=14 g_seq=26 -GoLabel dt=1 label_string=2 -GoBlock dt=13 reason_string=15 stack=5 -GoUnblock dt=65 g=29 g_seq=29 stack=0 -GoStart dt=8 g=29 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=18 reason_string=15 stack=5 -GoUnblock dt=13 g=29 g_seq=31 stack=0 -GoStart dt=6 g=29 g_seq=32 -GoLabel dt=2 label_string=2 -GoBlock dt=21 reason_string=15 stack=5 -GoUnblock dt=19 g=24 g_seq=37 stack=0 -GoStart dt=4 g=24 g_seq=38 -GoLabel dt=2 label_string=2 -GoBlock dt=33 reason_string=15 stack=5 -GoUnblock dt=8 g=24 g_seq=39 stack=0 -GoStart dt=3 g=24 g_seq=40 -GoLabel dt=1 label_string=2 -GoBlock dt=32 reason_string=15 stack=5 -GoUnblock dt=80 g=25 g_seq=29 stack=0 -GoStart dt=9 g=25 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=20 reason_string=15 stack=5 -GoUnblock dt=27 g=24 g_seq=43 stack=0 -GoStart dt=6 g=24 g_seq=44 -GoLabel dt=1 label_string=2 -GoBlock dt=185 reason_string=15 stack=5 -GoUnblock dt=9 g=24 g_seq=45 stack=0 -GoStart dt=6 g=24 g_seq=46 -GoLabel dt=3 label_string=2 -GoBlock dt=10 reason_string=15 stack=5 -GoUnblock dt=6 g=24 g_seq=47 stack=0 -GoStart dt=1 g=24 g_seq=48 -GoLabel dt=1 label_string=2 -GoBlock dt=41 reason_string=15 stack=5 -ProcStop dt=59 -ProcStart dt=21430 p=4 p_seq=1 -GoStart dt=238 g=102 g_seq=4 -GCMarkAssistEnd dt=10 -HeapAlloc dt=38 heapalloc_value=196352464 -GoStop dt=5526 reason_string=16 stack=6 -ProcStop dt=240 -ProcStart dt=11401 p=6 p_seq=1 -GoStart dt=196 g=109 g_seq=7 -GCMarkAssistEnd dt=5 -HeapAlloc dt=54 heapalloc_value=108264736 -HeapAlloc dt=117 heapalloc_value=108527008 -HeapAlloc dt=77 heapalloc_value=108783776 -HeapAlloc dt=90 heapalloc_value=109036320 -HeapAlloc dt=77 heapalloc_value=109355808 -HeapAlloc dt=106 heapalloc_value=109678240 -HeapAlloc dt=70 heapalloc_value=110030624 -HeapAlloc dt=90 heapalloc_value=110205056 -HeapAlloc dt=51 heapalloc_value=110347136 -HeapAlloc dt=63 heapalloc_value=110588800 -HeapAlloc dt=69 heapalloc_value=110912384 -HeapAlloc dt=42 heapalloc_value=111111808 -HeapAlloc dt=105 heapalloc_value=111452032 -HeapAlloc dt=89 heapalloc_value=111822720 -HeapAlloc dt=106 heapalloc_value=112260352 -HeapAlloc dt=55 heapalloc_value=112397056 -HeapAlloc dt=62 heapalloc_value=112682368 -HeapAlloc dt=137 heapalloc_value=113281920 -GCSweepBegin dt=50 stack=28 -GCSweepEnd dt=8 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=4 heapalloc_value=113424000 -HeapAlloc dt=92 heapalloc_value=113908096 -GCSweepBegin dt=145 stack=31 -EventBatch gen=3 m=169394 time=28114950898962 size=373 -ProcStatus dt=1 p=20 pstatus=1 -GoStatus dt=4 g=108 m=169394 gstatus=2 -GCMarkAssistActive dt=1 g=108 -GCMarkAssistEnd dt=2 -HeapAlloc dt=25 heapalloc_value=191102400 -GCMarkAssistBegin dt=104 stack=3 -GCMarkAssistEnd dt=2445 -HeapAlloc dt=47 heapalloc_value=191372736 -GCMarkAssistBegin dt=11 stack=3 -GoBlock dt=1789 reason_string=13 stack=11 -GoUnblock dt=19 g=22 g_seq=3 stack=0 -GoStart dt=7 g=22 g_seq=4 -GoLabel dt=1 label_string=2 -GoBlock dt=3342 reason_string=15 stack=5 -GoUnblock dt=2752 g=71 g_seq=1 stack=0 -GoStart dt=7 g=71 g_seq=2 -GoLabel dt=1 label_string=4 -GoBlock dt=269 reason_string=15 stack=5 -GoStart dt=4308 g=111 g_seq=3 -GCMarkAssistEnd dt=7 -HeapAlloc dt=58 heapalloc_value=191888832 -GCMarkAssistBegin dt=42 stack=3 -GoBlock dt=148 reason_string=13 stack=11 -GoUnblock dt=1120 g=72 g_seq=25 stack=0 -GoStart dt=5 g=72 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=640 reason_string=15 stack=5 -GoStart dt=1105 g=102 g_seq=2 -GoStatus dt=19 g=117 m=18446744073709551615 gstatus=4 -GoUnblock dt=4 g=117 g_seq=1 stack=10 -GCMarkAssistBegin dt=13 stack=3 -GoBlock dt=32 reason_string=13 stack=11 -GoStart dt=8 g=117 g_seq=2 -GoStatus dt=19 g=128 m=18446744073709551615 gstatus=4 -GoUnblock dt=2 g=128 g_seq=1 stack=10 -GCMarkAssistBegin dt=5 stack=3 -GoBlock dt=15 reason_string=13 stack=11 -GoStart dt=5 g=128 g_seq=2 -GoStatus dt=12 g=92 m=18446744073709551615 gstatus=4 -GoUnblock dt=1 g=92 g_seq=1 stack=10 -GCMarkAssistBegin dt=9 stack=3 -GoBlock dt=14 reason_string=13 stack=11 -GoStart dt=7 g=92 g_seq=2 -GoStatus dt=17 g=101 m=18446744073709551615 gstatus=4 -GoUnblock dt=1 g=101 g_seq=1 stack=10 -GCMarkAssistBegin dt=7 stack=3 -GoBlock dt=10 reason_string=13 stack=11 -GoStart dt=5 g=101 g_seq=2 -GoStatus dt=11 g=99 m=18446744073709551615 gstatus=4 -GoUnblock dt=1 g=99 g_seq=1 stack=10 -GCMarkAssistBegin dt=8 stack=3 -GoBlock dt=15 reason_string=13 stack=11 -GoStart dt=6 g=99 g_seq=2 -GoStatus dt=11 g=89 m=18446744073709551615 gstatus=4 -GoUnblock dt=1 g=89 g_seq=1 stack=10 -GCMarkAssistBegin dt=10 stack=3 -GoBlock dt=15 reason_string=13 stack=11 -GoStart dt=4 g=89 g_seq=2 -GoStatus dt=11 g=124 m=18446744073709551615 gstatus=4 -GoUnblock dt=2 g=124 g_seq=1 stack=10 -GCMarkAssistBegin dt=8 stack=3 -GoBlock dt=34 reason_string=13 stack=11 -GoStart dt=5 g=124 g_seq=2 -GoStatus dt=10 g=96 m=18446744073709551615 gstatus=4 -GoUnblock dt=1 g=96 g_seq=1 stack=10 -GCMarkAssistBegin dt=4 stack=3 -GoBlock dt=14 reason_string=13 stack=11 -GoStart dt=4 g=96 g_seq=2 -GCMarkAssistBegin dt=8 stack=3 -GoBlock dt=22 reason_string=13 stack=11 -ProcStop dt=16 -EventBatch gen=3 m=169393 time=28114950894837 size=271 -ProcStatus dt=2 p=16 pstatus=1 -GoStatus dt=2 g=69 m=169393 gstatus=2 -GoBlock dt=122 reason_string=15 stack=5 -GoStatus dt=2224 g=83 m=169393 gstatus=1 -GoStart dt=1 g=83 g_seq=1 -GoStatus dt=33 g=121 m=18446744073709551615 gstatus=4 -GoUnblock dt=10 g=121 g_seq=1 stack=10 -GCMarkAssistBegin dt=16 stack=3 -GoStop dt=620 reason_string=20 stack=9 -GoStart dt=11 g=121 g_seq=2 -GoStatus dt=18 g=110 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=110 g_seq=1 stack=10 -GCMarkAssistBegin dt=12 stack=3 -GoStop dt=1840 reason_string=20 stack=9 -GoStart dt=16 g=110 g_seq=2 -GoStatus dt=19 g=125 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=125 g_seq=1 stack=10 -GCMarkAssistBegin dt=10 stack=3 -GoBlock dt=1799 reason_string=13 stack=11 -GoStart dt=1317 g=127 g_seq=2 -GoStatus dt=21 g=116 m=18446744073709551615 gstatus=4 -GoUnblock dt=9 g=116 g_seq=1 stack=10 -GCMarkAssistBegin dt=16 stack=3 -GoBlock dt=473 reason_string=13 stack=11 -GoStart dt=28 g=116 g_seq=2 -GoStatus dt=14 g=119 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=119 g_seq=1 stack=10 -GCMarkAssistBegin dt=12 stack=3 -GoStop dt=570 reason_string=20 stack=9 -GoStart dt=24 g=119 g_seq=2 -GoStatus dt=18 g=95 m=18446744073709551615 gstatus=4 -GoUnblock dt=3 g=95 g_seq=1 stack=10 -GCMarkAssistBegin dt=11 stack=3 -GoBlock dt=5206 reason_string=13 stack=11 -ProcStop dt=2547 -ProcStart dt=26 p=16 p_seq=1 -GoUnblock dt=87 g=58 g_seq=15 stack=0 -GoStart dt=8 g=58 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=579 reason_string=15 stack=5 -GoUnblock dt=23 g=69 g_seq=15 stack=0 -GoStart dt=5 g=69 g_seq=16 -GoLabel dt=1 label_string=2 -GoBlock dt=1028 reason_string=15 stack=5 -GoUnblock dt=2356 g=14 g_seq=11 stack=0 -GoStart dt=6 g=14 g_seq=12 -GoLabel dt=1 label_string=4 -GoBlock dt=1282 reason_string=15 stack=5 -ProcStop dt=8 -EventBatch gen=3 m=169392 time=28114950898262 size=651 -ProcStatus dt=1 p=3 pstatus=1 -GoStatus dt=1 g=106 m=169392 gstatus=2 -GCMarkAssistActive dt=1 g=106 -GCMarkAssistEnd dt=3 -HeapAlloc dt=34 heapalloc_value=190807488 -HeapAlloc dt=125 heapalloc_value=190832064 -GCMarkAssistBegin dt=46 stack=3 -GoBlock dt=1002 reason_string=13 stack=11 -GoStart dt=28 g=82 g_seq=2 -GoBlock dt=1446 reason_string=13 stack=11 -GoStart dt=34 g=120 g_seq=3 -GCMarkAssistEnd dt=2 -HeapAlloc dt=32 heapalloc_value=191282624 -GCMarkAssistBegin dt=115 stack=3 -GoBlock dt=25 reason_string=13 stack=11 -GoStart dt=17 g=112 g_seq=2 -GoBlock dt=2074 reason_string=13 stack=11 -GoUnblock dt=2604 g=24 g_seq=5 stack=0 -GoStart dt=7 g=24 g_seq=6 -GoLabel dt=2 label_string=4 -GoBlock dt=278 reason_string=15 stack=5 -GoUnblock dt=2267 g=58 g_seq=5 stack=0 -GoStart dt=9 g=58 g_seq=6 -GoLabel dt=1 label_string=4 -GoBlock dt=316 reason_string=15 stack=5 -GoUnblock dt=1167 g=24 g_seq=7 stack=0 -GoStart dt=6 g=24 g_seq=8 -GoLabel dt=1 label_string=4 -GoBlock dt=171 reason_string=15 stack=5 -GoUnblock dt=1155 g=71 g_seq=7 stack=0 -GoStart dt=6 g=71 g_seq=8 -GoLabel dt=1 label_string=4 -GoBlock dt=32 reason_string=15 stack=5 -GoStart dt=3316 g=109 g_seq=2 -GoStatus dt=28 g=114 m=18446744073709551615 gstatus=4 -GoUnblock dt=8 g=114 g_seq=1 stack=10 -GCMarkAssistBegin dt=18 stack=3 -GoStop dt=3860 reason_string=20 stack=9 -GoUnblock dt=14 g=57 g_seq=31 stack=0 -GoStart dt=5 g=57 g_seq=32 -GoLabel dt=3 label_string=2 -GoBlock dt=3324 reason_string=15 stack=5 -GoUnblock dt=97 g=24 g_seq=25 stack=0 -GoStart dt=6 g=24 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=1146 reason_string=15 stack=5 -GoUnblock dt=73 g=24 g_seq=27 stack=0 -GoStart dt=4 g=24 g_seq=28 -GoLabel dt=1 label_string=4 -GoUnblock dt=2655 g=81 g_seq=4 stack=12 -GoBlock dt=402 reason_string=15 stack=5 -GoUnblock dt=9 g=24 g_seq=29 stack=0 -GoStart dt=7 g=24 g_seq=30 -GoLabel dt=1 label_string=2 -GoBlock dt=492 reason_string=15 stack=5 -GoUnblock dt=21 g=69 g_seq=27 stack=0 -GoStart dt=6 g=69 g_seq=28 -GoLabel dt=1 label_string=2 -GoBlock dt=20 reason_string=15 stack=5 -GoUnblock dt=11 g=69 g_seq=29 stack=0 -GoStart dt=3 g=69 g_seq=30 -GoLabel dt=1 label_string=2 -GoBlock dt=459 reason_string=15 stack=5 -GoStart dt=168 g=116 g_seq=6 -GCMarkAssistEnd dt=8 -HeapAlloc dt=61 heapalloc_value=192232224 -GCMarkAssistBegin dt=39 stack=3 -GoBlock dt=2360 reason_string=13 stack=11 -ProcStop dt=53 -ProcStart dt=14760 p=10 p_seq=2 -GoStart dt=211 g=99 g_seq=5 -GCMarkAssistBegin dt=93 stack=3 -GoBlock dt=33 reason_string=13 stack=11 -GoStart dt=9 g=120 g_seq=6 -GCMarkAssistBegin dt=78 stack=3 -GoBlock dt=102 reason_string=13 stack=11 -GoStart dt=31 g=108 g_seq=2 -GCMarkAssistEnd dt=6 -HeapAlloc dt=307 heapalloc_value=194853592 -GoStop dt=7166 reason_string=16 stack=6 -GoStart dt=86 g=128 g_seq=6 -HeapAlloc dt=4873 heapalloc_value=196688336 -GoStop dt=12 reason_string=16 stack=6 -ProcStop dt=395 -ProcStart dt=8670 p=3 p_seq=2 -GoStart dt=193 g=93 g_seq=9 -GCMarkAssistEnd dt=7 -HeapAlloc dt=78 heapalloc_value=104465440 -HeapAlloc dt=122 heapalloc_value=104583584 -HeapAlloc dt=92 heapalloc_value=104769312 -HeapAlloc dt=127 heapalloc_value=104935968 -GCSweepBegin dt=109 stack=28 -GCSweepEnd dt=9 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=2 heapalloc_value=105138720 -HeapAlloc dt=77 heapalloc_value=105373856 -GCSweepBegin dt=157 stack=28 -GCSweepEnd dt=8 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=3 heapalloc_value=105708448 -GCSweepBegin dt=56 stack=28 -GCSweepEnd dt=11 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=4 heapalloc_value=105880480 -GCSweepBegin dt=48 stack=28 -GCSweepEnd dt=10 swept_value=32768 reclaimed_value=32768 -HeapAlloc dt=4 heapalloc_value=106124192 -GCSweepBegin dt=79 stack=28 -GCSweepEnd dt=7 swept_value=8192 reclaimed_value=8192 -HeapAlloc dt=2 heapalloc_value=106283168 -HeapAlloc dt=98 heapalloc_value=106567968 -HeapAlloc dt=116 heapalloc_value=107070496 -HeapAlloc dt=30 heapalloc_value=107146272 -HeapAlloc dt=105 heapalloc_value=107517728 -HeapAlloc dt=169 heapalloc_value=108084512 -HeapAlloc dt=187 heapalloc_value=108649888 -HeapAlloc dt=158 heapalloc_value=109200160 -HeapAlloc dt=200 heapalloc_value=109872160 -GCSweepBegin dt=116 stack=28 -GCSweepEnd dt=9 swept_value=24576 reclaimed_value=24576 -HeapAlloc dt=3 heapalloc_value=110229632 -HeapAlloc dt=54 heapalloc_value=110441344 -HeapAlloc dt=76 heapalloc_value=110711680 -HeapAlloc dt=100 heapalloc_value=111216768 -HeapAlloc dt=156 heapalloc_value=111708032 -HeapAlloc dt=55 heapalloc_value=111972224 -HeapAlloc dt=122 heapalloc_value=112391424 -HeapAlloc dt=160 heapalloc_value=113099392 -HeapAlloc dt=191 heapalloc_value=113713536 -HeapAlloc dt=158 heapalloc_value=114362368 -GCSweepBegin dt=88 stack=28 -GCSweepEnd dt=14 swept_value=16384 reclaimed_value=16384 -HeapAlloc dt=9 heapalloc_value=114520320 -HeapAlloc dt=56 heapalloc_value=114636672 -GCSweepBegin dt=180 stack=27 -EventBatch gen=3 m=169390 time=28114950895313 size=834 -ProcStatus dt=1 p=27 pstatus=1 -GoStatus dt=3 g=82 m=169390 gstatus=2 -GCMarkAssistActive dt=1 g=82 -GCMarkAssistEnd dt=2 -HeapAlloc dt=28 heapalloc_value=190143936 -HeapAlloc dt=270 heapalloc_value=190201280 -HeapAlloc dt=96 heapalloc_value=190209472 -HeapAlloc dt=29 heapalloc_value=190258624 -HeapAlloc dt=107 heapalloc_value=190356928 -GCMarkAssistBegin dt=57 stack=3 -GCMarkAssistEnd dt=502 -HeapAlloc dt=27 heapalloc_value=190430656 -GoStop dt=26 reason_string=16 stack=4 -GoStart dt=12 g=131 g_seq=3 -GoSyscallBegin dt=17 p_seq=1 stack=7 -GoSyscallEnd dt=205 -GoSyscallBegin dt=19 p_seq=2 stack=7 -GoSyscallEnd dt=2580 -GoSyscallBegin dt=16 p_seq=3 stack=7 -GoSyscallEnd dt=71 -GoSyscallBegin dt=15 p_seq=4 stack=7 -GoSyscallEnd dt=72 -GoSyscallBegin dt=25 p_seq=5 stack=7 -GoSyscallEnd dt=76 -GoSyscallBegin dt=12 p_seq=6 stack=7 -GoSyscallEnd dt=69 -GoSyscallBegin dt=11 p_seq=7 stack=7 -GoSyscallEnd dt=62 -GoSyscallBegin dt=13 p_seq=8 stack=7 -GoSyscallEnd dt=67 -GoSyscallBegin dt=16 p_seq=9 stack=7 -GoSyscallEnd dt=64 -GoSyscallBegin dt=12 p_seq=10 stack=7 -GoSyscallEnd dt=65 -GoSyscallBegin dt=14 p_seq=11 stack=7 -GoSyscallEnd dt=226 -GoSyscallBegin dt=14 p_seq=12 stack=7 -GoSyscallEnd dt=69 -GoSyscallBegin dt=17 p_seq=13 stack=7 -GoSyscallEnd dt=72 -GoSyscallBegin dt=15 p_seq=14 stack=7 -GoSyscallEnd dt=66 -GoSyscallBegin dt=18 p_seq=15 stack=7 -GoSyscallEnd dt=63 -GoSyscallBegin dt=13 p_seq=16 stack=7 -GoSyscallEnd dt=69 -GoSyscallBegin dt=17 p_seq=17 stack=7 -GoSyscallEnd dt=66 -GoSyscallBegin dt=109 p_seq=18 stack=7 -GoSyscallEnd dt=73 -GoSyscallBegin dt=13 p_seq=19 stack=7 -GoSyscallEnd dt=68 -GoSyscallBegin dt=16 p_seq=20 stack=7 -GoSyscallEnd dt=63 -GoSyscallBegin dt=15 p_seq=21 stack=7 -GoSyscallEnd dt=82 -GoSyscallBegin dt=11 p_seq=22 stack=7 -GoSyscallEnd dt=177 -GoSyscallBegin dt=14 p_seq=23 stack=7 -GoSyscallEnd dt=62 -GoSyscallBegin dt=13 p_seq=24 stack=7 -GoSyscallEnd dt=90 -GoSyscallBegin dt=11 p_seq=25 stack=7 -GoSyscallEnd dt=69 -GoSyscallBegin dt=13 p_seq=26 stack=7 -GoSyscallEnd dt=65 -GoSyscallBegin dt=15 p_seq=27 stack=7 -GoSyscallEnd dt=72 -GoSyscallBegin dt=15 p_seq=28 stack=7 -GoSyscallEnd dt=73 -GoSyscallBegin dt=18 p_seq=29 stack=7 -GoSyscallEnd dt=80 -GoSyscallBegin dt=21 p_seq=30 stack=7 -GoSyscallEnd dt=72 -GoSyscallBegin dt=17 p_seq=31 stack=7 -GoSyscallEnd dt=67 -GoSyscallBegin dt=12 p_seq=32 stack=7 -GoSyscallEnd dt=171 -GoSyscallBegin dt=16 p_seq=33 stack=7 -GoSyscallEnd dt=76 -GoSyscallBegin dt=18 p_seq=34 stack=7 -GoSyscallEnd dt=78 -GoSyscallBegin dt=13 p_seq=35 stack=7 -GoSyscallEnd dt=77 -GoSyscallBegin dt=20 p_seq=36 stack=7 -GoSyscallEnd dt=77 -GoBlock dt=16 reason_string=15 stack=2 -GoUnblock dt=1400 g=54 g_seq=3 stack=0 -GoStart dt=8 g=54 g_seq=4 -GoLabel dt=1 label_string=4 -GoBlock dt=2659 reason_string=15 stack=5 -GoUnblock dt=13 g=22 g_seq=5 stack=0 -GoStart dt=5 g=22 g_seq=6 -GoLabel dt=1 label_string=2 -GoBlock dt=2498 reason_string=15 stack=5 -GoUnblock dt=10 g=22 g_seq=7 stack=0 -GoStart dt=7 g=22 g_seq=8 -GoLabel dt=2 label_string=2 -GoBlock dt=4213 reason_string=15 stack=5 -GoUnblock dt=1324 g=57 g_seq=25 stack=0 -GoStart dt=11 g=57 g_seq=26 -GoLabel dt=1 label_string=4 -GoBlock dt=256 reason_string=15 stack=5 -GoUnblock dt=8 g=57 g_seq=27 stack=0 -GoStart dt=5 g=57 g_seq=28 -GoLabel dt=1 label_string=2 -GoBlock dt=485 reason_string=15 stack=5 -GoUnblock dt=8 g=57 g_seq=29 stack=0 -GoStart dt=6 g=57 g_seq=30 -GoLabel dt=3 label_string=2 -GoBlock dt=504 reason_string=15 stack=5 -ProcStop dt=3771 -ProcStart dt=29 p=27 p_seq=37 -GoUnblock dt=9 g=22 g_seq=15 stack=0 -GoStart dt=5 g=22 g_seq=16 -GoLabel dt=1 label_string=4 -GoBlock dt=123 reason_string=15 stack=5 -GoUnblock dt=19 g=28 g_seq=7 stack=0 -GoStart dt=2 g=28 g_seq=8 -GoLabel dt=1 label_string=2 -GoBlock dt=67 reason_string=15 stack=5 -GoUnblock dt=73 g=72 g_seq=29 stack=0 -GoStart dt=8 g=72 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=1357 reason_string=15 stack=5 -GoUnblock dt=71 g=53 g_seq=15 stack=0 -GoStart dt=5 g=53 g_seq=16 -GoLabel dt=2 label_string=4 -GoBlock dt=53 reason_string=15 stack=5 -ProcStop dt=61 -ProcStart dt=29 p=27 p_seq=38 -GoUnblock dt=4 g=72 g_seq=35 stack=0 -GoStart dt=4 g=72 g_seq=36 -GoLabel dt=1 label_string=4 -GoBlock dt=775 reason_string=15 stack=5 -GoUnblock dt=11 g=72 g_seq=37 stack=0 -GoStart dt=5 g=72 g_seq=38 -GoLabel dt=3 label_string=2 -GoBlock dt=2553 reason_string=15 stack=5 -GoUnblock dt=23 g=54 g_seq=27 stack=0 -GoStart dt=7 g=54 g_seq=28 -GoLabel dt=1 label_string=2 -GoBlock dt=5185 reason_string=15 stack=5 -ProcStop dt=46 -ProcStart dt=1102 p=27 p_seq=39 -GoUnblock dt=17 g=14 g_seq=31 stack=0 -GoStart dt=191 g=14 g_seq=32 -GoLabel dt=5 label_string=2 -GoBlock dt=26 reason_string=15 stack=5 -GoUnblock dt=7 g=14 g_seq=33 stack=0 -GoStart dt=2 g=14 g_seq=34 -GoLabel dt=1 label_string=2 -GoBlock dt=81 reason_string=15 stack=5 -GoUnblock dt=11 g=14 g_seq=35 stack=0 -GoStart dt=6 g=14 g_seq=36 -GoLabel dt=1 label_string=2 -GoUnblock dt=257 g=97 g_seq=3 stack=12 -GoStop dt=1123 reason_string=16 stack=13 -GoUnblock dt=612 g=131 g_seq=4 stack=0 -GoStart dt=5 g=131 g_seq=5 -GoSyscallBegin dt=23 p_seq=40 stack=7 -GoSyscallEnd dt=200 -GoSyscallBegin dt=13 p_seq=41 stack=7 -GoSyscallEnd dt=179 -GoBlock dt=6 reason_string=15 stack=2 -ProcStop dt=31 -ProcStart dt=1232 p=22 p_seq=3 -GoUnblock dt=16 g=14 g_seq=40 stack=0 -GoStart dt=157 g=14 g_seq=41 -GoLabel dt=2 label_string=2 -GoUnblock dt=343 g=103 g_seq=1 stack=12 -GoBlock dt=2805 reason_string=15 stack=5 -ProcStop dt=68 -ProcStart dt=17 p=22 p_seq=4 -GoUnblock dt=3 g=14 g_seq=42 stack=0 -GoStart dt=4 g=14 g_seq=43 -GoLabel dt=1 label_string=4 -GoUnblock dt=609 g=116 g_seq=7 stack=12 -GoBlock dt=9 reason_string=15 stack=5 -GoStart dt=10 g=116 g_seq=8 -GCMarkAssistEnd dt=7 -HeapAlloc dt=60 heapalloc_value=192527064 -GCMarkAssistBegin dt=41 stack=3 -GoBlock dt=47 reason_string=13 stack=11 -GoUnblock dt=13 g=30 g_seq=35 stack=0 -GoStart dt=4 g=30 g_seq=36 -GoLabel dt=2 label_string=2 -GoBlock dt=266 reason_string=15 stack=5 -GoStart dt=16 g=105 g_seq=8 -GoBlock dt=18 reason_string=13 stack=11 -GoUnblock dt=55 g=54 g_seq=29 stack=0 -GoStart dt=8 g=54 g_seq=30 -GoLabel dt=1 label_string=4 -GoBlock dt=13 reason_string=15 stack=5 -GoUnblock dt=10 g=54 g_seq=31 stack=0 -GoStart dt=1 g=54 g_seq=32 -GoLabel dt=1 label_string=2 -GoBlock dt=46 reason_string=15 stack=5 -ProcStop dt=57 -ProcStart dt=14 p=22 p_seq=5 -GoUnblock dt=4 g=54 g_seq=33 stack=0 -GoStart dt=159 g=54 g_seq=34 -GoLabel dt=1 label_string=4 -GoBlock dt=8 reason_string=15 stack=5 -ProcStop dt=32 -ProcStart dt=3156 p=29 p_seq=1 -GoUnblock dt=15 g=71 g_seq=43 stack=0 -GoStart dt=165 g=71 g_seq=44 -GoLabel dt=1 label_string=2 -GoBlock dt=1463 reason_string=15 stack=5 -GoStart dt=22 g=118 g_seq=4 -GCMarkAssistEnd dt=6 -HeapAlloc dt=903 heapalloc_value=195328728 -GoStop dt=6525 reason_string=16 stack=6 -GoStart dt=46 g=118 g_seq=5 -GCMarkAssistBegin dt=12 stack=3 -GoBlock dt=31 reason_string=13 stack=11 -ProcStop dt=194 -EventBatch gen=3 m=18446744073709551615 time=28114950975784 size=435 -GoStatus dt=1 g=1 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=2 m=18446744073709551615 gstatus=4 -GoStatus dt=6 g=4 m=18446744073709551615 gstatus=4 -GoStatus dt=5 g=5 m=18446744073709551615 gstatus=4 -GoStatus dt=4 g=6 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=7 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=17 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=33 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=8 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=9 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=10 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=18 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=11 m=18446744073709551615 gstatus=4 -GoStatus dt=4 g=34 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=19 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=12 m=18446744073709551615 gstatus=4 -GoStatus dt=2 g=20 m=18446744073709551615 gstatus=4 -GoStatus dt=4 g=35 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=13 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=21 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=36 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=49 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=50 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=15 m=18446744073709551615 gstatus=4 -GoStatus dt=4 g=65 m=18446744073709551615 gstatus=4 -GoStatus dt=2 g=66 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=26 m=18446744073709551615 gstatus=4 -GoStatus dt=4 g=55 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=27 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=37 m=18446744073709551615 gstatus=4 -GoStatus dt=3 g=129 m=18446744073709551615 gstatus=4 -EventBatch gen=3 m=18446744073709551615 time=28114950976078 size=1132 -Stacks -Stack id=20 nframes=2 - pc=4540421 func=22 file=23 line=363 - pc=4546157 func=24 file=23 line=874 -Stack id=21 nframes=5 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=18 nframes=6 - pc=4296626 func=34 file=35 line=807 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=26 nframes=7 - pc=4300939 func=36 file=35 line=1196 - pc=4297301 func=34 file=35 line=926 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=7 nframes=7 - pc=4709082 func=37 file=38 line=964 - pc=4738119 func=39 file=40 line=209 - pc=4738111 func=41 file=42 line=736 - pc=4737664 func=43 file=42 line=380 - pc=4739536 func=44 file=45 line=46 - pc=4739528 func=46 file=47 line=183 - pc=4803162 func=48 file=49 line=134 -Stack id=10 nframes=4 - pc=4295522 func=50 file=35 line=627 - pc=4246870 func=29 file=28 line=1288 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=29 nframes=8 - pc=4556437 func=51 file=52 line=352 - pc=4341796 func=53 file=54 line=521 - pc=4279859 func=55 file=56 line=127 - pc=4277746 func=57 file=58 line=182 - pc=4244580 func=59 file=28 line=944 - pc=4245653 func=29 file=28 line=1116 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=14 nframes=1 - pc=4546157 func=24 file=23 line=874 -Stack id=17 nframes=1 - pc=0 func=0 file=0 line=0 -Stack id=19 nframes=2 - pc=4540420 func=22 file=23 line=353 - pc=4546157 func=24 file=23 line=874 -Stack id=13 nframes=1 - pc=0 func=0 file=0 line=0 -Stack id=5 nframes=2 - pc=4418893 func=60 file=61 line=402 - pc=4301860 func=62 file=35 line=1297 -Stack id=25 nframes=7 - pc=4298957 func=36 file=35 line=1087 - pc=4297301 func=34 file=35 line=926 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=4 nframes=2 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=30 nframes=6 - pc=4297308 func=34 file=35 line=817 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=11 nframes=6 - pc=4314276 func=63 file=26 line=749 - pc=4312530 func=25 file=26 line=589 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=6 nframes=2 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=15 nframes=1 - pc=4546157 func=24 file=23 line=874 -Stack id=8 nframes=1 - pc=0 func=0 file=0 line=0 -Stack id=12 nframes=2 - pc=4614055 func=64 file=65 line=474 - pc=4302129 func=62 file=35 line=1357 -Stack id=3 nframes=6 - pc=4556897 func=66 file=52 line=378 - pc=4312252 func=25 file=26 line=536 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=9 nframes=5 - pc=4312495 func=25 file=26 line=576 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=24 nframes=8 - pc=4614055 func=64 file=65 line=474 - pc=4298031 func=36 file=35 line=964 - pc=4297301 func=34 file=35 line=926 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=23 nframes=6 - pc=4297239 func=34 file=35 line=914 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=2 nframes=1 - pc=4803172 func=48 file=49 line=130 -Stack id=28 nframes=8 - pc=4556437 func=51 file=52 line=352 - pc=4341796 func=53 file=54 line=521 - pc=4280028 func=55 file=56 line=147 - pc=4277746 func=57 file=58 line=182 - pc=4244580 func=59 file=28 line=944 - pc=4246070 func=29 file=28 line=1145 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=27 nframes=5 - pc=4353658 func=67 file=68 line=958 - pc=4278148 func=69 file=58 line=234 - pc=4246244 func=29 file=28 line=1160 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=16 nframes=3 - pc=4217457 func=70 file=71 line=442 - pc=4546317 func=72 file=23 line=918 - pc=4546150 func=24 file=23 line=871 -Stack id=31 nframes=8 - pc=4353658 func=67 file=68 line=958 - pc=4280657 func=73 file=56 line=254 - pc=4280247 func=55 file=56 line=170 - pc=4277746 func=57 file=58 line=182 - pc=4244580 func=59 file=28 line=944 - pc=4246070 func=29 file=28 line=1145 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -Stack id=1 nframes=3 - pc=4554859 func=74 file=52 line=255 - pc=4540633 func=22 file=23 line=391 - pc=4546157 func=24 file=23 line=874 -Stack id=22 nframes=10 - pc=4558967 func=75 file=76 line=166 - pc=4558898 func=77 file=52 line=445 - pc=4447453 func=78 file=61 line=3712 - pc=4314041 func=79 file=26 line=714 - pc=4297238 func=34 file=35 line=909 - pc=4312466 func=25 file=26 line=564 - pc=4247187 func=27 file=28 line=1333 - pc=4245160 func=29 file=28 line=1021 - pc=4502184 func=30 file=31 line=103 - pc=4804475 func=32 file=33 line=60 -EventBatch gen=3 m=18446744073709551615 time=28114950894688 size=2762 -Strings -String id=1 - data="Not worker" -String id=2 - data="GC (dedicated)" -String id=3 - data="GC (fractional)" -String id=4 - data="GC (idle)" -String id=5 - data="unspecified" -String id=6 - data="forever" -String id=7 - data="network" -String id=8 - data="select" -String id=9 - data="sync.(*Cond).Wait" -String id=10 - data="sync" -String id=11 - data="chan send" -String id=12 - data="chan receive" -String id=13 - data="GC mark assist wait for work" -String id=14 - data="GC background sweeper wait" -String id=15 - data="system goroutine wait" -String id=16 - data="preempted" -String id=17 - data="wait for debug call" -String id=18 - data="wait until GC ends" -String id=19 - data="sleep" -String id=20 - data="runtime.Gosched" -String id=21 - data="GC mark termination" -String id=22 - data="runtime.traceAdvance" -String id=23 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" -String id=24 - data="runtime.(*traceAdvancerState).start.func1" -String id=25 - data="runtime.gcAssistAlloc" -String id=26 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgcmark.go" -String id=27 - data="runtime.deductAssistCredit" -String id=28 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/malloc.go" -String id=29 - data="runtime.mallocgc" -String id=30 - data="runtime.makeslice" -String id=31 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/slice.go" -String id=32 - data="main.main.func1" -String id=33 - data="/usr/local/google/home/mknyszek/work/go-1/src/internal/trace/v2/testdata/testprog/gc-stress.go" -String id=34 - data="runtime.gcMarkDone" -String id=35 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgc.go" -String id=36 - data="runtime.gcMarkTermination" -String id=37 - data="syscall.write" -String id=38 - data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/zsyscall_linux_amd64.go" -String id=39 - data="syscall.Write" -String id=40 - data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/syscall_unix.go" -String id=41 - data="internal/poll.ignoringEINTRIO" -String id=42 - data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_unix.go" -String id=43 - data="internal/poll.(*FD).Write" -String id=44 - data="os.(*File).write" -String id=45 - data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_posix.go" -String id=46 - data="os.(*File).Write" -String id=47 - data="/usr/local/google/home/mknyszek/work/go-1/src/os/file.go" -String id=48 - data="runtime/trace.Start.func1" -String id=49 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" -String id=50 - data="runtime.gcStart" -String id=51 - data="runtime.traceLocker.GCSweepSpan" -String id=52 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" -String id=53 - data="runtime.(*sweepLocked).sweep" -String id=54 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgcsweep.go" -String id=55 - data="runtime.(*mcentral).cacheSpan" -String id=56 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mcentral.go" -String id=57 - data="runtime.(*mcache).refill" -String id=58 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mcache.go" -String id=59 - data="runtime.(*mcache).nextFree" -String id=60 - data="runtime.gopark" -String id=61 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" -String id=62 - data="runtime.gcBgMarkWorker" -String id=63 - data="runtime.gcParkAssist" -String id=64 - data="runtime.systemstack_switch" -String id=65 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/asm_amd64.s" -String id=66 - data="runtime.traceLocker.GCMarkAssistStart" -String id=67 - data="runtime.(*mheap).alloc" -String id=68 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mheap.go" -String id=69 - data="runtime.(*mcache).allocLarge" -String id=70 - data="runtime.chanrecv1" -String id=71 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" -String id=72 - data="runtime.(*wakeableSleep).sleep" -String id=73 - data="runtime.(*mcentral).grow" -String id=74 - data="runtime.traceLocker.Gomaxprocs" -String id=75 - data="runtime.traceLocker.stack" -String id=76 - data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2event.go" -String id=77 - data="runtime.traceLocker.GoUnpark" -String id=78 - data="runtime.injectglist" -String id=79 - data="runtime.gcWakeAllAssists" diff --git a/src/internal/trace/v2/testdata/tests/go122-go-create-without-running-g.test b/src/internal/trace/v2/testdata/tests/go122-go-create-without-running-g.test deleted file mode 100644 index 494c444ca3..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-go-create-without-running-g.test +++ /dev/null @@ -1,17 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=17 -ProcStatus dt=1 p=0 pstatus=1 -GoCreate dt=1 new_g=5 new_stack=0 stack=0 -GoStart dt=1 g=5 g_seq=1 -GoStop dt=1 reason_string=1 stack=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=12 -Strings -String id=1 - data="whatever" diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-ambiguous.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-ambiguous.test deleted file mode 100644 index 0d88af4d61..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-ambiguous.test +++ /dev/null @@ -1,21 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=21 -ProcStatus dt=0 p=0 pstatus=1 -GoStatus dt=0 g=1 m=0 gstatus=2 -GoSyscallBegin dt=0 p_seq=1 stack=0 -GoSyscallEnd dt=0 -GoSyscallBegin dt=0 p_seq=2 stack=0 -GoSyscallEndBlocked dt=0 -EventBatch gen=1 m=1 time=0 size=14 -ProcStatus dt=0 p=2 pstatus=1 -GoStatus dt=0 g=2 m=1 gstatus=2 -ProcSteal dt=0 p=0 p_seq=3 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test deleted file mode 100644 index bbfc9cc877..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-bare-m.test +++ /dev/null @@ -1,17 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=11 -ProcStatus dt=1 p=1 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=3 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=9 -ProcStatus dt=1 p=0 pstatus=4 -ProcSteal dt=1 p=0 p_seq=1 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test deleted file mode 100644 index 8e291327cf..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.test +++ /dev/null @@ -1,18 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=15 -GoStatus dt=1 g=1 m=0 gstatus=3 -ProcStatus dt=1 p=1 pstatus=2 -ProcStart dt=1 p=1 p_seq=1 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=9 -ProcStatus dt=1 p=0 pstatus=4 -ProcSteal dt=1 p=0 p_seq=1 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test deleted file mode 100644 index 3b26e8f13f..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.test +++ /dev/null @@ -1,20 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=15 -GoStatus dt=1 g=1 m=0 gstatus=3 -ProcStatus dt=1 p=1 pstatus=2 -ProcStart dt=1 p=1 p_seq=1 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=18 -ProcStatus dt=1 p=2 pstatus=1 -GoStatus dt=1 g=2 m=1 gstatus=2 -ProcStatus dt=1 p=0 pstatus=4 -ProcSteal dt=1 p=0 p_seq=1 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary.test deleted file mode 100644 index 133d8a5447..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-gen-boundary.test +++ /dev/null @@ -1,19 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=11 -ProcStatus dt=1 p=1 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=3 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=18 -ProcStatus dt=1 p=2 pstatus=1 -GoStatus dt=1 g=2 m=1 gstatus=2 -ProcStatus dt=1 p=0 pstatus=4 -ProcSteal dt=1 p=0 p_seq=1 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test deleted file mode 100644 index fa68c824b9..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc-bare-m.test +++ /dev/null @@ -1,19 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=23 -ProcStatus dt=1 p=1 pstatus=2 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=2 -GoSyscallBegin dt=1 p_seq=1 stack=0 -ProcStart dt=1 p=1 p_seq=1 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=5 -ProcSteal dt=1 p=0 p_seq=2 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test deleted file mode 100644 index 85c19fcf05..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-reacquire-new-proc.test +++ /dev/null @@ -1,21 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=23 -ProcStatus dt=1 p=1 pstatus=2 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=2 -GoSyscallBegin dt=1 p_seq=1 stack=0 -ProcStart dt=1 p=1 p_seq=1 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=14 -ProcStatus dt=1 p=2 pstatus=1 -GoStatus dt=1 g=2 m=1 gstatus=2 -ProcSteal dt=1 p=0 p_seq=2 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-self.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-self.test deleted file mode 100644 index 6484eb6d35..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-self.test +++ /dev/null @@ -1,17 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=24 -ProcStatus dt=0 p=0 pstatus=1 -GoStatus dt=0 g=1 m=0 gstatus=2 -GoSyscallBegin dt=0 p_seq=1 stack=0 -ProcSteal dt=0 p=0 p_seq=2 m=0 -ProcStart dt=0 p=0 p_seq=3 -GoSyscallEndBlocked dt=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test deleted file mode 100644 index d33872287d..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple-bare-m.test +++ /dev/null @@ -1,17 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=15 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=2 -GoSyscallBegin dt=1 p_seq=1 stack=0 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=5 -ProcSteal dt=1 p=0 p_seq=2 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple.test deleted file mode 100644 index a1f9db41d4..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-simple.test +++ /dev/null @@ -1,19 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=15 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=2 -GoSyscallBegin dt=1 p_seq=1 stack=0 -GoSyscallEndBlocked dt=1 -EventBatch gen=1 m=1 time=0 size=14 -ProcStatus dt=1 p=2 pstatus=1 -GoStatus dt=1 g=2 m=1 gstatus=2 -ProcSteal dt=1 p=0 p_seq=2 m=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test deleted file mode 100644 index 58c41c5549..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-sitting-in-syscall.test +++ /dev/null @@ -1,15 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=9 -ProcStatus dt=1 p=0 pstatus=4 -ProcSteal dt=1 p=0 p_seq=1 m=1 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -GoStatus dt=1 g=1 m=1 gstatus=3 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-task-across-generations.test b/src/internal/trace/v2/testdata/tests/go122-task-across-generations.test deleted file mode 100644 index 0b8abd7346..0000000000 --- a/src/internal/trace/v2/testdata/tests/go122-task-across-generations.test +++ /dev/null @@ -1,26 +0,0 @@ --- expect -- -SUCCESS --- trace -- -Trace Go1.22 -EventBatch gen=1 m=0 time=0 size=15 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=2 -UserTaskBegin dt=1 task=2 parent_task=0 name_string=1 stack=0 -EventBatch gen=1 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=1 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=1 m=18446744073709551615 time=0 size=11 -Strings -String id=1 - data="my task" -EventBatch gen=2 m=0 time=5 size=13 -ProcStatus dt=1 p=0 pstatus=1 -GoStatus dt=1 g=1 m=0 gstatus=2 -UserTaskEnd dt=1 task=2 stack=0 -EventBatch gen=2 m=18446744073709551615 time=0 size=5 -Frequency freq=15625000 -EventBatch gen=2 m=18446744073709551615 time=0 size=1 -Stacks -EventBatch gen=2 m=18446744073709551615 time=0 size=1 -Strings diff --git a/src/internal/trace/v2/testtrace/expectation.go b/src/internal/trace/v2/testtrace/expectation.go deleted file mode 100644 index 3e5394a7e4..0000000000 --- a/src/internal/trace/v2/testtrace/expectation.go +++ /dev/null @@ -1,81 +0,0 @@ -// 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. - -package testtrace - -import ( - "bufio" - "bytes" - "fmt" - "regexp" - "strconv" - "strings" -) - -// Expectation represents the expected result of some operation. -type Expectation struct { - failure bool - errorMatcher *regexp.Regexp -} - -// ExpectSuccess returns an Expectation that trivially expects success. -func ExpectSuccess() *Expectation { - return new(Expectation) -} - -// Check validates whether err conforms to the expectation. Returns -// an error if it does not conform. -// -// Conformance means that if failure is true, then err must be non-nil. -// If err is non-nil, then it must match errorMatcher. -func (e *Expectation) Check(err error) error { - if !e.failure && err != nil { - return fmt.Errorf("unexpected error while reading the trace: %v", err) - } - if e.failure && err == nil { - return fmt.Errorf("expected error while reading the trace: want something matching %q, got none", e.errorMatcher) - } - if e.failure && err != nil && !e.errorMatcher.MatchString(err.Error()) { - return fmt.Errorf("unexpected error while reading the trace: want something matching %q, got %s", e.errorMatcher, err.Error()) - } - return nil -} - -// ParseExpectation parses the serialized form of an Expectation. -func ParseExpectation(data []byte) (*Expectation, error) { - exp := new(Expectation) - s := bufio.NewScanner(bytes.NewReader(data)) - if s.Scan() { - c := strings.SplitN(s.Text(), " ", 2) - switch c[0] { - case "SUCCESS": - case "FAILURE": - exp.failure = true - if len(c) != 2 { - return exp, fmt.Errorf("bad header line for FAILURE: %q", s.Text()) - } - matcher, err := parseMatcher(c[1]) - if err != nil { - return exp, err - } - exp.errorMatcher = matcher - default: - return exp, fmt.Errorf("bad header line: %q", s.Text()) - } - return exp, nil - } - return exp, s.Err() -} - -func parseMatcher(quoted string) (*regexp.Regexp, error) { - pattern, err := strconv.Unquote(quoted) - if err != nil { - return nil, fmt.Errorf("malformed pattern: not correctly quoted: %s: %v", quoted, err) - } - matcher, err := regexp.Compile(pattern) - if err != nil { - return nil, fmt.Errorf("malformed pattern: not a valid regexp: %s: %v", pattern, err) - } - return matcher, nil -} diff --git a/src/internal/trace/v2/testtrace/format.go b/src/internal/trace/v2/testtrace/format.go deleted file mode 100644 index 2e2e97530c..0000000000 --- a/src/internal/trace/v2/testtrace/format.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -package testtrace - -import ( - "bytes" - "fmt" - "internal/trace/v2/raw" - "internal/txtar" - "io" -) - -// ParseFile parses a test file generated by the testgen package. -func ParseFile(testPath string) (io.Reader, *Expectation, error) { - ar, err := txtar.ParseFile(testPath) - if err != nil { - return nil, nil, fmt.Errorf("failed to read test file for %s: %v", testPath, err) - } - if len(ar.Files) != 2 { - return nil, nil, fmt.Errorf("malformed test %s: wrong number of files", testPath) - } - if ar.Files[0].Name != "expect" { - return nil, nil, fmt.Errorf("malformed test %s: bad filename %s", testPath, ar.Files[0].Name) - } - if ar.Files[1].Name != "trace" { - return nil, nil, fmt.Errorf("malformed test %s: bad filename %s", testPath, ar.Files[1].Name) - } - tr, err := raw.NewTextReader(bytes.NewReader(ar.Files[1].Data)) - if err != nil { - return nil, nil, fmt.Errorf("malformed test %s: bad trace file: %v", testPath, err) - } - var buf bytes.Buffer - tw, err := raw.NewWriter(&buf, tr.Version()) - if err != nil { - return nil, nil, fmt.Errorf("failed to create trace byte writer: %v", err) - } - for { - ev, err := tr.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - return nil, nil, fmt.Errorf("malformed test %s: bad trace file: %v", testPath, err) - } - if err := tw.WriteEvent(ev); err != nil { - return nil, nil, fmt.Errorf("internal error during %s: failed to write trace bytes: %v", testPath, err) - } - } - exp, err := ParseExpectation(ar.Files[0].Data) - if err != nil { - return nil, nil, fmt.Errorf("internal error during %s: failed to parse expectation %q: %v", testPath, string(ar.Files[0].Data), err) - } - return &buf, exp, nil -} diff --git a/src/internal/trace/v2/testtrace/validation.go b/src/internal/trace/v2/testtrace/validation.go deleted file mode 100644 index 1dba366f5b..0000000000 --- a/src/internal/trace/v2/testtrace/validation.go +++ /dev/null @@ -1,379 +0,0 @@ -// 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. - -package testtrace - -import ( - "errors" - "fmt" - "internal/trace/v2" - "slices" - "strings" -) - -// Validator is a type used for validating a stream of trace.Events. -type Validator struct { - lastTs trace.Time - gs map[trace.GoID]*goState - ps map[trace.ProcID]*procState - ms map[trace.ThreadID]*schedContext - ranges map[trace.ResourceID][]string - tasks map[trace.TaskID]string - seenSync bool - Go121 bool -} - -type schedContext struct { - M trace.ThreadID - P trace.ProcID - G trace.GoID -} - -type goState struct { - state trace.GoState - binding *schedContext -} - -type procState struct { - state trace.ProcState - binding *schedContext -} - -// NewValidator creates a new Validator. -func NewValidator() *Validator { - return &Validator{ - gs: make(map[trace.GoID]*goState), - ps: make(map[trace.ProcID]*procState), - ms: make(map[trace.ThreadID]*schedContext), - ranges: make(map[trace.ResourceID][]string), - tasks: make(map[trace.TaskID]string), - } -} - -// Event validates ev as the next event in a stream of trace.Events. -// -// Returns an error if validation fails. -func (v *Validator) Event(ev trace.Event) error { - e := new(errAccumulator) - - // Validate timestamp order. - if v.lastTs != 0 { - if ev.Time() <= v.lastTs { - e.Errorf("timestamp out-of-order for %+v", ev) - } else { - v.lastTs = ev.Time() - } - } else { - v.lastTs = ev.Time() - } - - // Validate event stack. - checkStack(e, ev.Stack()) - - switch ev.Kind() { - case trace.EventSync: - // Just record that we've seen a Sync at some point. - v.seenSync = true - case trace.EventMetric: - m := ev.Metric() - if !strings.Contains(m.Name, ":") { - // Should have a ":" as per runtime/metrics convention. - e.Errorf("invalid metric name %q", m.Name) - } - // Make sure the value is OK. - if m.Value.Kind() == trace.ValueBad { - e.Errorf("invalid value") - } - switch m.Value.Kind() { - case trace.ValueUint64: - // Just make sure it doesn't panic. - _ = m.Value.Uint64() - } - case trace.EventLabel: - l := ev.Label() - - // Check label. - if l.Label == "" { - e.Errorf("invalid label %q", l.Label) - } - - // Check label resource. - if l.Resource.Kind == trace.ResourceNone { - e.Errorf("label resource none") - } - switch l.Resource.Kind { - case trace.ResourceGoroutine: - id := l.Resource.Goroutine() - if _, ok := v.gs[id]; !ok { - e.Errorf("label for invalid goroutine %d", id) - } - case trace.ResourceProc: - id := l.Resource.Proc() - if _, ok := v.ps[id]; !ok { - e.Errorf("label for invalid proc %d", id) - } - case trace.ResourceThread: - id := l.Resource.Thread() - if _, ok := v.ms[id]; !ok { - e.Errorf("label for invalid thread %d", id) - } - } - case trace.EventStackSample: - // Not much to check here. It's basically a sched context and a stack. - // The sched context is also not guaranteed to align with other events. - // We already checked the stack above. - case trace.EventStateTransition: - // Validate state transitions. - // - // TODO(mknyszek): A lot of logic is duplicated between goroutines and procs. - // The two are intentionally handled identically; from the perspective of the - // API, resources all have the same general properties. Consider making this - // code generic over resources and implementing validation just once. - tr := ev.StateTransition() - checkStack(e, tr.Stack) - switch tr.Resource.Kind { - case trace.ResourceGoroutine: - // Basic state transition validation. - id := tr.Resource.Goroutine() - old, new := tr.Goroutine() - if new == trace.GoUndetermined { - e.Errorf("transition to undetermined state for goroutine %d", id) - } - if v.seenSync && old == trace.GoUndetermined { - e.Errorf("undetermined goroutine %d after first global sync", id) - } - if new == trace.GoNotExist && v.hasAnyRange(trace.MakeResourceID(id)) { - e.Errorf("goroutine %d died with active ranges", id) - } - state, ok := v.gs[id] - if ok { - if old != state.state { - e.Errorf("bad old state for goroutine %d: got %s, want %s", id, old, state.state) - } - state.state = new - } else { - if old != trace.GoUndetermined && old != trace.GoNotExist { - e.Errorf("bad old state for unregistered goroutine %d: %s", id, old) - } - state = &goState{state: new} - v.gs[id] = state - } - // Validate sched context. - if new.Executing() { - ctx := v.getOrCreateThread(e, ev, ev.Thread()) - if ctx != nil { - if ctx.G != trace.NoGoroutine && ctx.G != id { - e.Errorf("tried to run goroutine %d when one was already executing (%d) on thread %d", id, ctx.G, ev.Thread()) - } - ctx.G = id - state.binding = ctx - } - } else if old.Executing() && !new.Executing() { - if tr.Stack != ev.Stack() { - // This is a case where the transition is happening to a goroutine that is also executing, so - // these two stacks should always match. - e.Errorf("StateTransition.Stack doesn't match Event.Stack") - } - ctx := state.binding - if ctx != nil { - if ctx.G != id { - e.Errorf("tried to stop goroutine %d when it wasn't currently executing (currently executing %d) on thread %d", id, ctx.G, ev.Thread()) - } - ctx.G = trace.NoGoroutine - state.binding = nil - } else { - e.Errorf("stopping goroutine %d not bound to any active context", id) - } - } - case trace.ResourceProc: - // Basic state transition validation. - id := tr.Resource.Proc() - old, new := tr.Proc() - if new == trace.ProcUndetermined { - e.Errorf("transition to undetermined state for proc %d", id) - } - if v.seenSync && old == trace.ProcUndetermined { - e.Errorf("undetermined proc %d after first global sync", id) - } - if new == trace.ProcNotExist && v.hasAnyRange(trace.MakeResourceID(id)) { - e.Errorf("proc %d died with active ranges", id) - } - state, ok := v.ps[id] - if ok { - if old != state.state { - e.Errorf("bad old state for proc %d: got %s, want %s", id, old, state.state) - } - state.state = new - } else { - if old != trace.ProcUndetermined && old != trace.ProcNotExist { - e.Errorf("bad old state for unregistered proc %d: %s", id, old) - } - state = &procState{state: new} - v.ps[id] = state - } - // Validate sched context. - if new.Executing() { - ctx := v.getOrCreateThread(e, ev, ev.Thread()) - if ctx != nil { - if ctx.P != trace.NoProc && ctx.P != id { - e.Errorf("tried to run proc %d when one was already executing (%d) on thread %d", id, ctx.P, ev.Thread()) - } - ctx.P = id - state.binding = ctx - } - } else if old.Executing() && !new.Executing() { - ctx := state.binding - if ctx != nil { - if ctx.P != id { - e.Errorf("tried to stop proc %d when it wasn't currently executing (currently executing %d) on thread %d", id, ctx.P, ctx.M) - } - ctx.P = trace.NoProc - state.binding = nil - } else { - e.Errorf("stopping proc %d not bound to any active context", id) - } - } - } - case trace.EventRangeBegin, trace.EventRangeActive, trace.EventRangeEnd: - // Validate ranges. - r := ev.Range() - switch ev.Kind() { - case trace.EventRangeBegin: - if v.hasRange(r.Scope, r.Name) { - e.Errorf("already active range %q on %v begun again", r.Name, r.Scope) - } - v.addRange(r.Scope, r.Name) - case trace.EventRangeActive: - if !v.hasRange(r.Scope, r.Name) { - v.addRange(r.Scope, r.Name) - } - case trace.EventRangeEnd: - if !v.hasRange(r.Scope, r.Name) { - e.Errorf("inactive range %q on %v ended", r.Name, r.Scope) - } - v.deleteRange(r.Scope, r.Name) - } - case trace.EventTaskBegin: - // Validate task begin. - t := ev.Task() - if t.ID == trace.NoTask || t.ID == trace.BackgroundTask { - // The background task should never have an event emitted for it. - e.Errorf("found invalid task ID for task of type %s", t.Type) - } - if t.Parent == trace.BackgroundTask { - // It's not possible for a task to be a subtask of the background task. - e.Errorf("found background task as the parent for task of type %s", t.Type) - } - // N.B. Don't check the task type. Empty string is a valid task type. - v.tasks[t.ID] = t.Type - case trace.EventTaskEnd: - // Validate task end. - // We can see a task end without a begin, so ignore a task without information. - // Instead, if we've seen the task begin, just make sure the task end lines up. - t := ev.Task() - if typ, ok := v.tasks[t.ID]; ok { - if t.Type != typ { - e.Errorf("task end type %q doesn't match task start type %q for task %d", t.Type, typ, t.ID) - } - delete(v.tasks, t.ID) - } - case trace.EventLog: - // There's really not much here to check, except that we can - // generate a Log. The category and message are entirely user-created, - // so we can't make any assumptions as to what they are. We also - // can't validate the task, because proving the task's existence is very - // much best-effort. - _ = ev.Log() - } - return e.Errors() -} - -func (v *Validator) hasRange(r trace.ResourceID, name string) bool { - ranges, ok := v.ranges[r] - return ok && slices.Contains(ranges, name) -} - -func (v *Validator) addRange(r trace.ResourceID, name string) { - ranges, _ := v.ranges[r] - ranges = append(ranges, name) - v.ranges[r] = ranges -} - -func (v *Validator) hasAnyRange(r trace.ResourceID) bool { - ranges, ok := v.ranges[r] - return ok && len(ranges) != 0 -} - -func (v *Validator) deleteRange(r trace.ResourceID, name string) { - ranges, ok := v.ranges[r] - if !ok { - return - } - i := slices.Index(ranges, name) - if i < 0 { - return - } - v.ranges[r] = slices.Delete(ranges, i, i+1) -} - -func (v *Validator) getOrCreateThread(e *errAccumulator, ev trace.Event, m trace.ThreadID) *schedContext { - lenient := func() bool { - // Be lenient about GoUndetermined -> GoSyscall transitions if they - // originate from an old trace. These transitions lack thread - // information in trace formats older than 1.22. - if !v.Go121 { - return false - } - if ev.Kind() != trace.EventStateTransition { - return false - } - tr := ev.StateTransition() - if tr.Resource.Kind != trace.ResourceGoroutine { - return false - } - from, to := tr.Goroutine() - return from == trace.GoUndetermined && to == trace.GoSyscall - } - if m == trace.NoThread && !lenient() { - e.Errorf("must have thread, but thread ID is none") - return nil - } - s, ok := v.ms[m] - if !ok { - s = &schedContext{M: m, P: trace.NoProc, G: trace.NoGoroutine} - v.ms[m] = s - return s - } - return s -} - -func checkStack(e *errAccumulator, stk trace.Stack) { - // Check for non-empty values, but we also check for crashes due to incorrect validation. - i := 0 - stk.Frames(func(f trace.StackFrame) bool { - if i == 0 { - // Allow for one fully zero stack. - // - // TODO(mknyszek): Investigate why that happens. - return true - } - if f.Func == "" || f.File == "" || f.PC == 0 || f.Line == 0 { - e.Errorf("invalid stack frame %#v: missing information", f) - } - i++ - return true - }) -} - -type errAccumulator struct { - errs []error -} - -func (e *errAccumulator) Errorf(f string, args ...any) { - e.errs = append(e.errs, fmt.Errorf(f, args...)) -} - -func (e *errAccumulator) Errors() error { - return errors.Join(e.errs...) -} diff --git a/src/internal/trace/v2/trace_test.go b/src/internal/trace/v2/trace_test.go deleted file mode 100644 index 9b74cc0d81..0000000000 --- a/src/internal/trace/v2/trace_test.go +++ /dev/null @@ -1,629 +0,0 @@ -// 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. - -package trace_test - -import ( - "bufio" - "bytes" - "fmt" - "internal/race" - "internal/testenv" - "internal/trace/v2" - "internal/trace/v2/testtrace" - "io" - "os" - "path/filepath" - "runtime" - "strings" - "testing" -) - -func TestTraceAnnotations(t *testing.T) { - testTraceProg(t, "annotations.go", func(t *testing.T, tb, _ []byte, _ bool) { - type evDesc struct { - kind trace.EventKind - task trace.TaskID - args []string - } - want := []evDesc{ - {trace.EventTaskBegin, trace.TaskID(1), []string{"task0"}}, - {trace.EventRegionBegin, trace.TaskID(1), []string{"region0"}}, - {trace.EventRegionBegin, trace.TaskID(1), []string{"region1"}}, - {trace.EventLog, trace.TaskID(1), []string{"key0", "0123456789abcdef"}}, - {trace.EventRegionEnd, trace.TaskID(1), []string{"region1"}}, - {trace.EventRegionEnd, trace.TaskID(1), []string{"region0"}}, - {trace.EventTaskEnd, trace.TaskID(1), []string{"task0"}}, - // Currently, pre-existing region is not recorded to avoid allocations. - {trace.EventRegionBegin, trace.BackgroundTask, []string{"post-existing region"}}, - } - r, err := trace.NewReader(bytes.NewReader(tb)) - if err != nil { - t.Error(err) - } - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - for i, wantEv := range want { - if wantEv.kind != ev.Kind() { - continue - } - match := false - switch ev.Kind() { - case trace.EventTaskBegin, trace.EventTaskEnd: - task := ev.Task() - match = task.ID == wantEv.task && task.Type == wantEv.args[0] - case trace.EventRegionBegin, trace.EventRegionEnd: - reg := ev.Region() - match = reg.Task == wantEv.task && reg.Type == wantEv.args[0] - case trace.EventLog: - log := ev.Log() - match = log.Task == wantEv.task && log.Category == wantEv.args[0] && log.Message == wantEv.args[1] - } - if match { - want[i] = want[len(want)-1] - want = want[:len(want)-1] - break - } - } - } - if len(want) != 0 { - for _, ev := range want { - t.Errorf("no match for %s TaskID=%d Args=%#v", ev.kind, ev.task, ev.args) - } - } - }) -} - -func TestTraceAnnotationsStress(t *testing.T) { - testTraceProg(t, "annotations-stress.go", nil) -} - -func TestTraceCgoCallback(t *testing.T) { - testenv.MustHaveCGO(t) - - switch runtime.GOOS { - case "plan9", "windows": - t.Skipf("cgo callback test requires pthreads and is not supported on %s", runtime.GOOS) - } - testTraceProg(t, "cgo-callback.go", nil) -} - -func TestTraceCPUProfile(t *testing.T) { - testTraceProg(t, "cpu-profile.go", func(t *testing.T, tb, stderr []byte, _ bool) { - // Parse stderr which has a CPU profile summary, if everything went well. - // (If it didn't, we shouldn't even make it here.) - scanner := bufio.NewScanner(bytes.NewReader(stderr)) - pprofSamples := 0 - pprofStacks := make(map[string]int) - for scanner.Scan() { - var stack string - var samples int - _, err := fmt.Sscanf(scanner.Text(), "%s\t%d", &stack, &samples) - if err != nil { - t.Fatalf("failed to parse CPU profile summary in stderr: %s\n\tfull:\n%s", scanner.Text(), stderr) - } - pprofStacks[stack] = samples - pprofSamples += samples - } - if err := scanner.Err(); err != nil { - t.Fatalf("failed to parse CPU profile summary in stderr: %v", err) - } - if pprofSamples == 0 { - t.Skip("CPU profile did not include any samples while tracing was active") - } - - // Examine the execution tracer's view of the CPU profile samples. Filter it - // to only include samples from the single test goroutine. Use the goroutine - // ID that was recorded in the events: that should reflect getg().m.curg, - // same as the profiler's labels (even when the M is using its g0 stack). - totalTraceSamples := 0 - traceSamples := 0 - traceStacks := make(map[string]int) - r, err := trace.NewReader(bytes.NewReader(tb)) - if err != nil { - t.Error(err) - } - var hogRegion *trace.Event - var hogRegionClosed bool - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - if ev.Kind() == trace.EventRegionBegin && ev.Region().Type == "cpuHogger" { - hogRegion = &ev - } - if ev.Kind() == trace.EventStackSample { - totalTraceSamples++ - if hogRegion != nil && ev.Goroutine() == hogRegion.Goroutine() { - traceSamples++ - var fns []string - ev.Stack().Frames(func(frame trace.StackFrame) bool { - if frame.Func != "runtime.goexit" { - fns = append(fns, fmt.Sprintf("%s:%d", frame.Func, frame.Line)) - } - return true - }) - stack := strings.Join(fns, "|") - traceStacks[stack]++ - } - } - if ev.Kind() == trace.EventRegionEnd && ev.Region().Type == "cpuHogger" { - hogRegionClosed = true - } - } - if hogRegion == nil { - t.Fatalf("execution trace did not identify cpuHogger goroutine") - } else if !hogRegionClosed { - t.Fatalf("execution trace did not close cpuHogger region") - } - - // The execution trace may drop CPU profile samples if the profiling buffer - // overflows. Based on the size of profBufWordCount, that takes a bit over - // 1900 CPU samples or 19 thread-seconds at a 100 Hz sample rate. If we've - // hit that case, then we definitely have at least one full buffer's worth - // of CPU samples, so we'll call that success. - overflowed := totalTraceSamples >= 1900 - if traceSamples < pprofSamples { - t.Logf("execution trace did not include all CPU profile samples; %d in profile, %d in trace", pprofSamples, traceSamples) - if !overflowed { - t.Fail() - } - } - - for stack, traceSamples := range traceStacks { - pprofSamples := pprofStacks[stack] - delete(pprofStacks, stack) - if traceSamples < pprofSamples { - t.Logf("execution trace did not include all CPU profile samples for stack %q; %d in profile, %d in trace", - stack, pprofSamples, traceSamples) - if !overflowed { - t.Fail() - } - } - } - for stack, pprofSamples := range pprofStacks { - t.Logf("CPU profile included %d samples at stack %q not present in execution trace", pprofSamples, stack) - if !overflowed { - t.Fail() - } - } - - if t.Failed() { - t.Logf("execution trace CPU samples:") - for stack, samples := range traceStacks { - t.Logf("%d: %q", samples, stack) - } - t.Logf("CPU profile:\n%s", stderr) - } - }) -} - -func TestTraceFutileWakeup(t *testing.T) { - testTraceProg(t, "futile-wakeup.go", func(t *testing.T, tb, _ []byte, _ bool) { - // Check to make sure that no goroutine in the "special" trace region - // ends up blocking, unblocking, then immediately blocking again. - // - // The goroutines are careful to call runtime.Gosched in between blocking, - // so there should never be a clean block/unblock on the goroutine unless - // the runtime was generating extraneous events. - const ( - entered = iota - blocked - runnable - running - ) - gs := make(map[trace.GoID]int) - seenSpecialGoroutines := false - r, err := trace.NewReader(bytes.NewReader(tb)) - if err != nil { - t.Error(err) - } - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - // Only track goroutines in the special region we control, so runtime - // goroutines don't interfere (it's totally valid in traces for a - // goroutine to block, run, and block again; that's not what we care about). - if ev.Kind() == trace.EventRegionBegin && ev.Region().Type == "special" { - seenSpecialGoroutines = true - gs[ev.Goroutine()] = entered - } - if ev.Kind() == trace.EventRegionEnd && ev.Region().Type == "special" { - delete(gs, ev.Goroutine()) - } - // Track state transitions for goroutines we care about. - // - // The goroutines we care about will advance through the state machine - // of entered -> blocked -> runnable -> running. If in the running state - // we block, then we have a futile wakeup. Because of the runtime.Gosched - // on these specially marked goroutines, we should end up back in runnable - // first. If at any point we go to a different state, switch back to entered - // and wait for the next time the goroutine blocks. - if ev.Kind() != trace.EventStateTransition { - continue - } - st := ev.StateTransition() - if st.Resource.Kind != trace.ResourceGoroutine { - continue - } - id := st.Resource.Goroutine() - state, ok := gs[id] - if !ok { - continue - } - _, new := st.Goroutine() - switch state { - case entered: - if new == trace.GoWaiting { - state = blocked - } else { - state = entered - } - case blocked: - if new == trace.GoRunnable { - state = runnable - } else { - state = entered - } - case runnable: - if new == trace.GoRunning { - state = running - } else { - state = entered - } - case running: - if new == trace.GoWaiting { - t.Fatalf("found futile wakeup on goroutine %d", id) - } else { - state = entered - } - } - gs[id] = state - } - if !seenSpecialGoroutines { - t.Fatal("did not see a goroutine in a the region 'special'") - } - }) -} - -func TestTraceGCStress(t *testing.T) { - testTraceProg(t, "gc-stress.go", nil) -} - -func TestTraceGOMAXPROCS(t *testing.T) { - testTraceProg(t, "gomaxprocs.go", nil) -} - -func TestTraceStacks(t *testing.T) { - testTraceProg(t, "stacks.go", func(t *testing.T, tb, _ []byte, stress bool) { - type frame struct { - fn string - line int - } - type evDesc struct { - kind trace.EventKind - match string - frames []frame - } - // mainLine is the line number of `func main()` in testprog/stacks.go. - const mainLine = 21 - want := []evDesc{ - {trace.EventStateTransition, "Goroutine Running->Runnable", []frame{ - {"main.main", mainLine + 82}, - }}, - {trace.EventStateTransition, "Goroutine NotExist->Runnable", []frame{ - {"main.main", mainLine + 11}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"runtime.block", 0}, - {"main.main.func1", 0}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"runtime.chansend1", 0}, - {"main.main.func2", 0}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"runtime.chanrecv1", 0}, - {"main.main.func3", 0}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"runtime.chanrecv1", 0}, - {"main.main.func4", 0}, - }}, - {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ - {"runtime.chansend1", 0}, - {"main.main", mainLine + 84}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"runtime.chansend1", 0}, - {"main.main.func5", 0}, - }}, - {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ - {"runtime.chanrecv1", 0}, - {"main.main", mainLine + 85}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"runtime.selectgo", 0}, - {"main.main.func6", 0}, - }}, - {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ - {"runtime.selectgo", 0}, - {"main.main", mainLine + 86}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"sync.(*Mutex).Lock", 0}, - {"main.main.func7", 0}, - }}, - {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ - {"sync.(*Mutex).Unlock", 0}, - {"main.main", 0}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"sync.(*WaitGroup).Wait", 0}, - {"main.main.func8", 0}, - }}, - {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ - {"sync.(*WaitGroup).Add", 0}, - {"sync.(*WaitGroup).Done", 0}, - {"main.main", mainLine + 91}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"sync.(*Cond).Wait", 0}, - {"main.main.func9", 0}, - }}, - {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ - {"sync.(*Cond).Signal", 0}, - {"main.main", 0}, - }}, - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"time.Sleep", 0}, - {"main.main", 0}, - }}, - {trace.EventMetric, "/sched/gomaxprocs:threads", []frame{ - {"runtime.startTheWorld", 0}, // this is when the current gomaxprocs is logged. - {"runtime.startTheWorldGC", 0}, - {"runtime.GOMAXPROCS", 0}, - {"main.main", 0}, - }}, - } - if !stress { - // Only check for this stack if !stress because traceAdvance alone could - // allocate enough memory to trigger a GC if called frequently enough. - // This might cause the runtime.GC call we're trying to match against to - // coalesce with an active GC triggered this by traceAdvance. In that case - // we won't have an EventRangeBegin event that matches the stace trace we're - // looking for, since runtime.GC will not have triggered the GC. - gcEv := evDesc{trace.EventRangeBegin, "GC concurrent mark phase", []frame{ - {"runtime.GC", 0}, - {"main.main", 0}, - }} - want = append(want, gcEv) - } - if runtime.GOOS != "windows" && runtime.GOOS != "plan9" { - want = append(want, []evDesc{ - {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ - {"internal/poll.(*FD).Accept", 0}, - {"net.(*netFD).accept", 0}, - {"net.(*TCPListener).accept", 0}, - {"net.(*TCPListener).Accept", 0}, - {"main.main.func10", 0}, - }}, - {trace.EventStateTransition, "Goroutine Running->Syscall", []frame{ - {"syscall.read", 0}, - {"syscall.Read", 0}, - {"internal/poll.ignoringEINTRIO", 0}, - {"internal/poll.(*FD).Read", 0}, - {"os.(*File).read", 0}, - {"os.(*File).Read", 0}, - {"main.main.func11", 0}, - }}, - }...) - } - stackMatches := func(stk trace.Stack, frames []frame) bool { - i := 0 - match := true - stk.Frames(func(f trace.StackFrame) bool { - if f.Func != frames[i].fn { - match = false - return false - } - if line := uint64(frames[i].line); line != 0 && line != f.Line { - match = false - return false - } - i++ - return true - }) - return match - } - r, err := trace.NewReader(bytes.NewReader(tb)) - if err != nil { - t.Error(err) - } - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - for i, wantEv := range want { - if wantEv.kind != ev.Kind() { - continue - } - match := false - switch ev.Kind() { - case trace.EventStateTransition: - st := ev.StateTransition() - str := "" - switch st.Resource.Kind { - case trace.ResourceGoroutine: - old, new := st.Goroutine() - str = fmt.Sprintf("%s %s->%s", st.Resource.Kind, old, new) - } - match = str == wantEv.match - case trace.EventRangeBegin: - rng := ev.Range() - match = rng.Name == wantEv.match - case trace.EventMetric: - metric := ev.Metric() - match = metric.Name == wantEv.match - } - match = match && stackMatches(ev.Stack(), wantEv.frames) - if match { - want[i] = want[len(want)-1] - want = want[:len(want)-1] - break - } - } - } - if len(want) != 0 { - for _, ev := range want { - t.Errorf("no match for %s Match=%s Stack=%#v", ev.kind, ev.match, ev.frames) - } - } - }) -} - -func TestTraceStress(t *testing.T) { - switch runtime.GOOS { - case "js", "wasip1": - t.Skip("no os.Pipe on " + runtime.GOOS) - } - testTraceProg(t, "stress.go", nil) -} - -func TestTraceStressStartStop(t *testing.T) { - switch runtime.GOOS { - case "js", "wasip1": - t.Skip("no os.Pipe on " + runtime.GOOS) - } - testTraceProg(t, "stress-start-stop.go", nil) -} - -func TestTraceManyStartStop(t *testing.T) { - testTraceProg(t, "many-start-stop.go", nil) -} - -func TestTraceWaitOnPipe(t *testing.T) { - switch runtime.GOOS { - case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": - testTraceProg(t, "wait-on-pipe.go", nil) - return - } - t.Skip("no applicable syscall.Pipe on " + runtime.GOOS) -} - -func TestTraceIterPull(t *testing.T) { - testTraceProg(t, "iter-pull.go", nil) -} - -func testTraceProg(t *testing.T, progName string, extra func(t *testing.T, trace, stderr []byte, stress bool)) { - testenv.MustHaveGoRun(t) - - // Check if we're on a builder. - onBuilder := testenv.Builder() != "" - onOldBuilder := !strings.Contains(testenv.Builder(), "gotip") && !strings.Contains(testenv.Builder(), "go1") - - testPath := filepath.Join("./testdata/testprog", progName) - testName := progName - runTest := func(t *testing.T, stress bool, extraGODEBUG string) { - // Run the program and capture the trace, which is always written to stdout. - cmd := testenv.Command(t, testenv.GoToolPath(t), "run") - if race.Enabled { - cmd.Args = append(cmd.Args, "-race") - } - cmd.Args = append(cmd.Args, testPath) - cmd.Env = append(os.Environ(), "GOEXPERIMENT=rangefunc") - // Add a stack ownership check. This is cheap enough for testing. - godebug := "tracecheckstackownership=1" - if stress { - // Advance a generation constantly to stress the tracer. - godebug += ",traceadvanceperiod=0" - } - if extraGODEBUG != "" { - // Add extra GODEBUG flags. - godebug += "," + extraGODEBUG - } - cmd.Env = append(cmd.Env, "GODEBUG="+godebug) - - // Capture stdout and stderr. - // - // The protocol for these programs is that stdout contains the trace data - // and stderr is an expectation in string format. - var traceBuf, errBuf bytes.Buffer - cmd.Stdout = &traceBuf - cmd.Stderr = &errBuf - // Run the program. - if err := cmd.Run(); err != nil { - if errBuf.Len() != 0 { - t.Logf("stderr: %s", string(errBuf.Bytes())) - } - t.Fatal(err) - } - tb := traceBuf.Bytes() - - // Test the trace and the parser. - testReader(t, bytes.NewReader(tb), testtrace.ExpectSuccess()) - - // Run some extra validation. - if !t.Failed() && extra != nil { - extra(t, tb, errBuf.Bytes(), stress) - } - - // Dump some more information on failure. - if t.Failed() && onBuilder { - // Dump directly to the test log on the builder, since this - // data is critical for debugging and this is the only way - // we can currently make sure it's retained. - t.Log("found bad trace; dumping to test log...") - s := dumpTraceToText(t, tb) - if onOldBuilder && len(s) > 1<<20+512<<10 { - // The old build infrastructure truncates logs at ~2 MiB. - // Let's assume we're the only failure and give ourselves - // up to 1.5 MiB to dump the trace. - // - // TODO(mknyszek): Remove this when we've migrated off of - // the old infrastructure. - t.Logf("text trace too large to dump (%d bytes)", len(s)) - } else { - t.Log(s) - } - } else if t.Failed() || *dumpTraces { - // We asked to dump the trace or failed. Write the trace to a file. - t.Logf("wrote trace to file: %s", dumpTraceToFile(t, testName, stress, tb)) - } - } - t.Run("Default", func(t *testing.T) { - runTest(t, false, "") - }) - t.Run("Stress", func(t *testing.T) { - if testing.Short() { - t.Skip("skipping trace stress tests in short mode") - } - runTest(t, true, "") - }) - t.Run("AllocFree", func(t *testing.T) { - if testing.Short() { - t.Skip("skipping trace alloc/free tests in short mode") - } - runTest(t, false, "traceallocfree=1") - }) -} diff --git a/src/internal/trace/v2/value.go b/src/internal/trace/v2/value.go deleted file mode 100644 index bd2cba7878..0000000000 --- a/src/internal/trace/v2/value.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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. - -package trace - -import "fmt" - -// Value is a dynamically-typed value obtained from a trace. -type Value struct { - kind ValueKind - scalar uint64 -} - -// ValueKind is the type of a dynamically-typed value from a trace. -type ValueKind uint8 - -const ( - ValueBad ValueKind = iota - ValueUint64 -) - -// Kind returns the ValueKind of the value. -// -// It represents the underlying structure of the value. -// -// New ValueKinds may be added in the future. Users of this type must be robust -// to that possibility. -func (v Value) Kind() ValueKind { - return v.kind -} - -// Uint64 returns the uint64 value for a MetricSampleUint64. -// -// Panics if this metric sample's Kind is not MetricSampleUint64. -func (v Value) Uint64() uint64 { - if v.kind != ValueUint64 { - panic("Uint64 called on Value of a different Kind") - } - return v.scalar -} - -// valueAsString produces a debug string value. -// -// This isn't just Value.String because we may want to use that to store -// string values in the future. -func valueAsString(v Value) string { - switch v.Kind() { - case ValueUint64: - return fmt.Sprintf("Uint64(%d)", v.scalar) - } - return "Bad" -} diff --git a/src/internal/trace/v2/version/version.go b/src/internal/trace/v2/version/version.go deleted file mode 100644 index e3354eb0c1..0000000000 --- a/src/internal/trace/v2/version/version.go +++ /dev/null @@ -1,71 +0,0 @@ -// 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. - -package version - -import ( - "fmt" - "io" - - "internal/trace/v2/event" - "internal/trace/v2/event/go122" -) - -// Version represents the version of a trace file. -type Version uint32 - -const ( - Go111 Version = 11 - Go119 Version = 19 - Go121 Version = 21 - Go122 Version = 22 - Go123 Version = 23 - Current = Go123 -) - -var versions = map[Version][]event.Spec{ - // Go 1.11–1.21 use a different parser and are only set here for the sake of - // Version.Valid. - Go111: nil, - Go119: nil, - Go121: nil, - - Go122: go122.Specs(), - // Go 1.23 adds backwards-incompatible events, but - // traces produced by Go 1.22 are also always valid - // Go 1.23 traces. - Go123: go122.Specs(), -} - -// Specs returns the set of event.Specs for this version. -func (v Version) Specs() []event.Spec { - return versions[v] -} - -func (v Version) Valid() bool { - _, ok := versions[v] - return ok -} - -// headerFmt is the format of the header of all Go execution traces. -const headerFmt = "go 1.%d trace\x00\x00\x00" - -// ReadHeader reads the version of the trace out of the trace file's -// header, whose prefix must be present in v. -func ReadHeader(r io.Reader) (Version, error) { - var v Version - _, err := fmt.Fscanf(r, headerFmt, &v) - if err != nil { - return v, fmt.Errorf("bad file format: not a Go execution trace?") - } - if !v.Valid() { - return v, fmt.Errorf("unknown or unsupported trace version go 1.%d", v) - } - return v, nil -} - -// WriteHeader writes a header for a trace version v to w. -func WriteHeader(w io.Writer, v Version) (int, error) { - return fmt.Fprintf(w, headerFmt, v) -} diff --git a/src/internal/trace/value.go b/src/internal/trace/value.go new file mode 100644 index 0000000000..bd2cba7878 --- /dev/null +++ b/src/internal/trace/value.go @@ -0,0 +1,53 @@ +// 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. + +package trace + +import "fmt" + +// Value is a dynamically-typed value obtained from a trace. +type Value struct { + kind ValueKind + scalar uint64 +} + +// ValueKind is the type of a dynamically-typed value from a trace. +type ValueKind uint8 + +const ( + ValueBad ValueKind = iota + ValueUint64 +) + +// Kind returns the ValueKind of the value. +// +// It represents the underlying structure of the value. +// +// New ValueKinds may be added in the future. Users of this type must be robust +// to that possibility. +func (v Value) Kind() ValueKind { + return v.kind +} + +// Uint64 returns the uint64 value for a MetricSampleUint64. +// +// Panics if this metric sample's Kind is not MetricSampleUint64. +func (v Value) Uint64() uint64 { + if v.kind != ValueUint64 { + panic("Uint64 called on Value of a different Kind") + } + return v.scalar +} + +// valueAsString produces a debug string value. +// +// This isn't just Value.String because we may want to use that to store +// string values in the future. +func valueAsString(v Value) string { + switch v.Kind() { + case ValueUint64: + return fmt.Sprintf("Uint64(%d)", v.scalar) + } + return "Bad" +} diff --git a/src/internal/trace/version/version.go b/src/internal/trace/version/version.go new file mode 100644 index 0000000000..4951bd97d7 --- /dev/null +++ b/src/internal/trace/version/version.go @@ -0,0 +1,71 @@ +// 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. + +package version + +import ( + "fmt" + "io" + + "internal/trace/event" + "internal/trace/event/go122" +) + +// Version represents the version of a trace file. +type Version uint32 + +const ( + Go111 Version = 11 + Go119 Version = 19 + Go121 Version = 21 + Go122 Version = 22 + Go123 Version = 23 + Current = Go123 +) + +var versions = map[Version][]event.Spec{ + // Go 1.11–1.21 use a different parser and are only set here for the sake of + // Version.Valid. + Go111: nil, + Go119: nil, + Go121: nil, + + Go122: go122.Specs(), + // Go 1.23 adds backwards-incompatible events, but + // traces produced by Go 1.22 are also always valid + // Go 1.23 traces. + Go123: go122.Specs(), +} + +// Specs returns the set of event.Specs for this version. +func (v Version) Specs() []event.Spec { + return versions[v] +} + +func (v Version) Valid() bool { + _, ok := versions[v] + return ok +} + +// headerFmt is the format of the header of all Go execution traces. +const headerFmt = "go 1.%d trace\x00\x00\x00" + +// ReadHeader reads the version of the trace out of the trace file's +// header, whose prefix must be present in v. +func ReadHeader(r io.Reader) (Version, error) { + var v Version + _, err := fmt.Fscanf(r, headerFmt, &v) + if err != nil { + return v, fmt.Errorf("bad file format: not a Go execution trace?") + } + if !v.Valid() { + return v, fmt.Errorf("unknown or unsupported trace version go 1.%d", v) + } + return v, nil +} + +// WriteHeader writes a header for a trace version v to w. +func WriteHeader(w io.Writer, v Version) (int, error) { + return fmt.Fprintf(w, headerFmt, v) +} -- cgit v1.3