diff --git a/make/test.mk b/make/test.mk index 312d78022f..5ae63b0da5 100644 --- a/make/test.mk +++ b/make/test.mk @@ -219,8 +219,51 @@ TEST_PACKAGES_SHORT = \ TEST_PACKAGES_SHORT_HOST := $(filter $(TEST_PACKAGES_SHORT),$(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)) TEST_PACKAGES_PRINTER_HOST := $(filter go/printer,$(TEST_PACKAGES_HOST)) -TEST_PACKAGES_ALLOCS_HOST := $(filter slices strings,$(TEST_PACKAGES_HOST)) -TEST_ALLOCS_SKIP_FLAG := -skip='^(TestBuilderAllocs|TestBuilderGrow|TestGrow)$$' +TEST_PACKAGES_ALLOC_SHA := crypto/sha256 crypto/sha512 +TEST_ALLOC_SHA_SKIP_FLAG := -skip='^(TestExtraMethods|TestAllocations|TestAllocatonsWithTypeAsserts)$$' +TEST_PACKAGES_ALLOC_STRCONV := strconv +TEST_ALLOC_STRCONV_SKIP_FLAG := -skip='^(TestCountMallocs|TestAllocationsFromBytes)$$' +TEST_PACKAGES_ALLOC_UNICODE := unicode/utf16 unicode/utf8 +TEST_ALLOC_UNICODE_SKIP_FLAG := -skip='^(TestAllocationsDecode|TestRuneCountNonASCIIAllocation)$$' +TEST_PACKAGES_ALLOC_CONTEXT := context +TEST_ALLOC_CONTEXT_SKIP_FLAG := -skip='^TestAllocs$$' +TEST_PACKAGES_ALLOC_MIME := mime +TEST_ALLOC_MIME_SKIP_FLAG := -skip='^TestLookupMallocs$$' +TEST_PACKAGES_ALLOC_TEXTPROTO := net/textproto +TEST_ALLOC_TEXTPROTO_SKIP_FLAG := -skip='^TestCommonHeaders$$' +TEST_PACKAGES_ALLOC_REFLECT := reflect +TEST_ALLOC_REFLECT_SKIP_FLAG := -skip='^(TestMapIterSet|TestDeepEqualAllocs)$$' +TEST_PACKAGES_ALLOC_BYTES := bytes +TEST_ALLOC_BYTES_SKIP_FLAG := -skip='^(TestNewBufferShallow|TestEqual|TestIndex|TestLastIndex|TestReplace)$$' +TEST_PACKAGES_ALLOC_SLICES := slices +TEST_ALLOC_SLICES_SKIP_FLAG := -skip='^(TestGrow|TestInsert|TestConcat)$$' +TEST_PACKAGES_ALLOC_STRINGS := strings +TEST_ALLOC_STRINGS_SKIP_FLAG := -skip='^(TestBuilderAllocs|TestBuilderGrow|TestBuilderGrowSizeclasses|TestIndexRune|TestReplace)$$' +TEST_PACKAGES_ALLOCS := \ + $(TEST_PACKAGES_ALLOC_SHA) \ + $(TEST_PACKAGES_ALLOC_STRCONV) \ + $(TEST_PACKAGES_ALLOC_UNICODE) \ + $(TEST_PACKAGES_ALLOC_CONTEXT) \ + $(TEST_PACKAGES_ALLOC_MIME) \ + $(TEST_PACKAGES_ALLOC_TEXTPROTO) \ + $(TEST_PACKAGES_ALLOC_REFLECT) \ + $(TEST_PACKAGES_ALLOC_BYTES) \ + $(TEST_PACKAGES_ALLOC_SLICES) \ + $(TEST_PACKAGES_ALLOC_STRINGS) \ + $(nil) + +define run-tinygo-alloc-tests +$(if $(filter $(TEST_PACKAGES_ALLOC_SHA),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_SHA_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_SHA),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_STRCONV),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_STRCONV_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_STRCONV),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_UNICODE),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_UNICODE_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_UNICODE),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_CONTEXT),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_CONTEXT_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_CONTEXT),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_MIME),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_MIME_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_MIME),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_TEXTPROTO),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_TEXTPROTO_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_TEXTPROTO),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_REFLECT),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_REFLECT_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_REFLECT),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_BYTES),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_BYTES_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_BYTES),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_SLICES),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_SLICES_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_SLICES),$(1))) +$(if $(filter $(TEST_PACKAGES_ALLOC_STRINGS),$(1)),$(3) $(TINYGO) test $(2) $(TEST_ALLOC_STRINGS_SKIP_FLAG) $(filter $(TEST_PACKAGES_ALLOC_STRINGS),$(1))) +endef TEST_PACKAGES_NETIP_HOST := $(filter net/netip,$(TEST_PACKAGES_HOST)) # Test known-working standard library packages. @@ -230,18 +273,16 @@ tinygo-test: @# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented. @# TestUnmarshalNestingLimit{Slice,Struct}: encoding/asn1 nesting limit added in @# https://github.com/golang/go/commit/6a6d115f9a7422b2fa081ba6f567eefb4a099462 - $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml $(TEST_PACKAGES_SHORT) $(TEST_PACKAGES_PRINTER_HOST) $(TEST_PACKAGES_ALLOCS_HOST) $(TEST_PACKAGES_NETIP_HOST),$(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)) + $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml $(TEST_PACKAGES_SHORT) $(TEST_PACKAGES_PRINTER_HOST) $(TEST_PACKAGES_ALLOCS) $(TEST_PACKAGES_NETIP_HOST),$(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)) ifneq ($(TEST_PACKAGES_SHORT_HOST),) $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -short $(TEST_PACKAGES_SHORT_HOST) endif ifneq ($(TEST_PACKAGES_PRINTER_HOST),) $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -stack-size=1MB $(TEST_PACKAGES_PRINTER_HOST) endif -ifneq ($(TEST_PACKAGES_ALLOCS_HOST),) - $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_ALLOCS_SKIP_FLAG) $(TEST_PACKAGES_ALLOCS_HOST) -endif + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW),$(TEST_ADDITIONAL_FLAGS)) ifneq ($(TEST_PACKAGES_NETIP_HOST),) - $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^TestAddrStringAllocs$$' $(TEST_PACKAGES_NETIP_HOST) + $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^TestAddrStringAllocs$$|^TestNoAllocs$$/^(Addr.IsGlobalUnicast|Addr.IsInterfaceLocalMulticast|Addr.IsLinkLocalMulticast|Addr.IsLinkLocalUnicast|Addr.IsPrivate)$$' $(TEST_PACKAGES_NETIP_HOST) endif $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^(TestReflectFuncOf|TestChannelMovedOutOfBubble|TestTimerFromInsideBubble|TestWaitGroupMovedIntoBubble|TestWaitGroupMovedOutOfBubble|TestWaitGroupMovedBetweenBubblesWithNonZeroCount)$$' internal/synctest $(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^(TestFatal|TestError|TestVerboseError|TestSkip|TestVerboseSkip|TestHelper|TestHTTPTransport100Continue)$$' testing/synctest @@ -256,9 +297,13 @@ ifeq ($(TEST_IOFS),true) $(TINYGO) test -stack-size=6MB io/fs endif tinygo-test-fast: - $(TINYGO) test $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS_HOST),$(TEST_PACKAGES_HOST)) -ifneq ($(TEST_PACKAGES_ALLOCS_HOST),) - $(TINYGO) test $(TEST_ALLOCS_SKIP_FLAG) $(TEST_PACKAGES_ALLOCS_HOST) + $(TINYGO) test $(TEST_SKIP_FLAG) $(filter-out encoding/xml $(TEST_PACKAGES_PRINTER_HOST) $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_HOST)) +ifneq ($(TEST_PACKAGES_PRINTER_HOST),) + $(TINYGO) test -stack-size=1MB $(TEST_PACKAGES_PRINTER_HOST) +endif + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_HOST)) +ifeq ($(TEST_ENCODING_XML),true) + $(TINYGO) test $(TEST_SKIP_FLAG) -short -stack-size=16MB encoding/xml endif tinygo-bench: $(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW) @@ -267,18 +312,24 @@ tinygo-bench-fast: # Same thing, except for wasi rather than the current platform. tinygo-test-wasm: - $(TINYGO) test -target wasm $(TEST_SKIP_FLAG) $(TEST_PACKAGES_WASM) + $(TINYGO) test -target wasm $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_WASM)) + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_WASM),-target wasm) tinygo-test-wasi: - $(TINYGO) test -target wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi + $(TINYGO) test -target wasip1 $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)) ./tests/runtime_wasi + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW),-target wasip1) tinygo-test-wasip1: - GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi + GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)) ./tests/runtime_wasi + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW),,GOOS=wasip1 GOARCH=wasm) tinygo-test-wasip1-fast: - $(TINYGO) test -target=wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASI) ./tests/runtime_wasi + $(TINYGO) test -target=wasip1 $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_FAST_WASI)) ./tests/runtime_wasi + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_FAST_WASI),-target=wasip1) tinygo-test-wasip2-slow: - $(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW) + $(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_SLOW)) + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_SLOW),-target=wasip2) tinygo-test-wasip2-fast: - $(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASIP2) ./tests/runtime_wasi + $(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_FAST_WASIP2)) ./tests/runtime_wasi + $(call run-tinygo-alloc-tests,$(TEST_PACKAGES_FAST_WASIP2),-target=wasip2) tinygo-test-wasip2-sum-slow: TINYGO=$(TINYGO) \ @@ -304,7 +355,10 @@ tinygo-bench-wasip2-fast: # Run tests on riscv-qemu since that one provides a large amount of memory. tinygo-test-baremetal: - $(TINYGO) test -target riscv-qemu $(TEST_SKIP_FLAG) $(TEST_PACKAGES_BAREMETAL) + $(TINYGO) test -target riscv-qemu $(TEST_SKIP_FLAG) $(filter-out $(TEST_PACKAGES_ALLOCS),$(TEST_PACKAGES_BAREMETAL)) + $(if $(filter $(TEST_PACKAGES_ALLOC_STRCONV),$(TEST_PACKAGES_BAREMETAL)),$(TINYGO) test -target riscv-qemu -skip='TestCountMallocs|TestAllocationsFromBytes' $(filter $(TEST_PACKAGES_ALLOC_STRCONV),$(TEST_PACKAGES_BAREMETAL))) + $(if $(filter $(TEST_PACKAGES_ALLOC_UNICODE),$(TEST_PACKAGES_BAREMETAL)),$(TINYGO) test -target riscv-qemu -skip='TestAllocationsDecode|TestRuneCountNonASCIIAllocation' $(filter $(TEST_PACKAGES_ALLOC_UNICODE),$(TEST_PACKAGES_BAREMETAL))) + $(if $(filter $(TEST_PACKAGES_ALLOC_REFLECT),$(TEST_PACKAGES_BAREMETAL)),$(TINYGO) test -target riscv-qemu -skip='TestMapIterSet|TestDeepEqualAllocs' $(filter $(TEST_PACKAGES_ALLOC_REFLECT),$(TEST_PACKAGES_BAREMETAL))) # Test external packages in a large corpus. test-corpus: diff --git a/src/runtime/gc_blocks.go b/src/runtime/gc_blocks.go index e93fea9f67..734994e8a0 100644 --- a/src/runtime/gc_blocks.go +++ b/src/runtime/gc_blocks.go @@ -912,6 +912,13 @@ func ReadMemStats(m *MemStats) { gcLock.Unlock() } +func mallocs() uint64 { + gcLock.Lock() + mallocs := gcMallocs + gcLock.Unlock() + return mallocs +} + // count4LUT is a lookup table used to count set bits in a 4-bit mask. // TODO: replace with popcnt when available var count4LUT = [16]uint8{ diff --git a/src/runtime/gc_boehm.go b/src/runtime/gc_boehm.go index 81592ec002..0e91e0f09e 100644 --- a/src/runtime/gc_boehm.go +++ b/src/runtime/gc_boehm.go @@ -32,7 +32,10 @@ const ( boehmNormalKind = 1 ) -var gcLock task.PMutex +var ( + gcLock task.PMutex + gcMallocs uint64 +) func initHeap() { libgc_init() @@ -74,6 +77,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { } gcLock.Lock() + gcMallocs++ var ptr unsafe.Pointer var needsZero bool switch layout { @@ -200,6 +204,13 @@ func ReadMemStats(m *MemStats) { gcLock.Unlock() } +func mallocs() uint64 { + gcLock.Lock() + mallocs := gcMallocs + gcLock.Unlock() + return mallocs +} + func setHeapEnd(newHeapEnd uintptr) { runtimeFatal("gc: did not expect setHeapEnd call") } diff --git a/src/runtime/gc_custom.go b/src/runtime/gc_custom.go index 4ac03c639c..da31e2717f 100644 --- a/src/runtime/gc_custom.go +++ b/src/runtime/gc_custom.go @@ -57,6 +57,12 @@ func SetFinalizer(obj interface{}, finalizer interface{}) // ReadMemStats populates m with memory statistics. func ReadMemStats(ms *MemStats) +func mallocs() uint64 { + var stats MemStats + ReadMemStats(&stats) + return stats.Mallocs +} + func setHeapEnd(newHeapEnd uintptr) { // Heap is in custom GC so ignore for when called from wasm initialization. } diff --git a/src/runtime/gc_leaking.go b/src/runtime/gc_leaking.go index e3ea66ebc4..1263fb5a91 100644 --- a/src/runtime/gc_leaking.go +++ b/src/runtime/gc_leaking.go @@ -106,6 +106,13 @@ func ReadMemStats(m *MemStats) { gcLock.Unlock() } +func mallocs() uint64 { + gcLock.Lock() + mallocs := gcMallocs + gcLock.Unlock() + return mallocs +} + func GC() { // No-op. } diff --git a/src/runtime/gc_none.go b/src/runtime/gc_none.go index e0d2b46d06..001bf8f01d 100644 --- a/src/runtime/gc_none.go +++ b/src/runtime/gc_none.go @@ -34,6 +34,10 @@ func GC() { // Unimplemented. } +func mallocs() uint64 { + return 0 +} + func markRoots(start, end uintptr) { runtimeFatal("unreachable: markRoots") } diff --git a/src/testing/allocs_test.go b/src/testing/allocs_test.go new file mode 100644 index 0000000000..1414ef7043 --- /dev/null +++ b/src/testing/allocs_test.go @@ -0,0 +1,23 @@ +package testing_test + +import "testing" + +var ( + allocations [101]*byte + allocation int +) + +func TestAllocsPerRun(t *testing.T) { + allocs := testing.AllocsPerRun(100, func() { + allocations[allocation] = new(byte) + allocation++ + }) + if allocs != 1 { + t.Errorf("got %v allocations, want 1", allocs) + } + for i := 1; i < len(allocations); i++ { + if allocations[i] == allocations[i-1] { + t.Fatalf("allocations %d and %d have the same address", i-1, i) + } + } +} diff --git a/src/testing/testing.go b/src/testing/testing.go index 114223518f..999dd2d25a 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -977,16 +977,23 @@ func (t *T) report() { // AllocsPerRun returns the average number of allocations during calls to f. // Although the return value has type float64, it will always be an integral // value. -// -// Not implemented. func AllocsPerRun(runs int, f func()) (avg float64) { f() + + mallocs := 0 - runtimeMallocs() + for range runs { f() } - return 0 + + mallocs += runtimeMallocs() + + return float64(mallocs / uint64(runs)) } +//go:linkname runtimeMallocs runtime.mallocs +func runtimeMallocs() uint64 + type InternalExample struct { Name string F func()