From 1843d5ff007c92f53b6ac25528e596cfcd8b5ae4 Mon Sep 17 00:00:00 2001 From: SYC_ Date: Fri, 26 Jan 2024 11:06:48 +0000 Subject: [PATCH] feat: support go1.22&go1.23 Change-Id: I58178d6d6c832f7dcf9a41b9821a6acff9f0a149 --- .github/workflows/tests.yml | 2 + internal/monkey/common/mem_above_1_19.go | 46 --------------- internal/monkey/common/page.go | 21 +++++-- .../stw/stw_1_21.go} | 26 +++++--- .../common/runtime_link/stw/stw_1_22.go | 57 ++++++++++++++++++ .../common/runtime_link/stw/stw_above_1_22.go | 59 +++++++++++++++++++ .../stw/stw_below_1_21.go} | 19 ++++-- .../monkey/common/runtime_link/stw/stw_ctx.go | 26 ++++++++ .../stw/stw_disable.go} | 28 +++++---- internal/monkey/mem/write.go | 6 +- internal/monkey/mem/write_darwin_test.go | 46 --------------- internal/monkey/mem/write_linux_test.go | 46 --------------- internal/monkey/mem/write_test.go | 19 ++++++ internal/tool/goroutine.go | 17 +++++- 14 files changed, 248 insertions(+), 170 deletions(-) delete mode 100644 internal/monkey/common/mem_above_1_19.go rename internal/monkey/common/{runtime_above_1_21.go => runtime_link/stw/stw_1_21.go} (69%) create mode 100644 internal/monkey/common/runtime_link/stw/stw_1_22.go create mode 100644 internal/monkey/common/runtime_link/stw/stw_above_1_22.go rename internal/monkey/common/{runtime_below_1_21.go => runtime_link/stw/stw_below_1_21.go} (75%) create mode 100644 internal/monkey/common/runtime_link/stw/stw_ctx.go rename internal/monkey/common/{mem_below_1_19.go => runtime_link/stw/stw_disable.go} (61%) delete mode 100644 internal/monkey/mem/write_darwin_test.go delete mode 100644 internal/monkey/mem/write_linux_test.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 643b291..1e2bdd5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,6 +16,8 @@ jobs: "1.19", "1.20", "1.21", + "1.22", + "1.23", ] os: [linux] # should be [ macOS, linux, windows ], but currently we don't have macOS and windows runners arch: [X64, ARM64] diff --git a/internal/monkey/common/mem_above_1_19.go b/internal/monkey/common/mem_above_1_19.go deleted file mode 100644 index fe58fa7..0000000 --- a/internal/monkey/common/mem_above_1_19.go +++ /dev/null @@ -1,46 +0,0 @@ -//go:build go1.19 -// +build go1.19 - -/* - * Copyright 2022 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package common - -import ( - "unsafe" - - "github.com/bytedance/mockey/internal/tool" -) - -/* - * This may lead to runtime.ReadMemStats inaccurate - * See https://github.com/bytedance/mockey/issues/13 - */ -func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer { - tool.DebugPrintf("sysAlloc: go version >= 1.19 use sysAllocOS") - return sysAllocOS(n) -} - -func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) { - tool.DebugPrintf("sysAlloc: go version >= 1.19 use sysFreeOS") - sysFreeOS(v, n) -} - -//go:linkname sysAllocOS runtime.sysAllocOS -func sysAllocOS(n uintptr) unsafe.Pointer - -//go:linkname sysFreeOS runtime.sysFreeOS -func sysFreeOS(v unsafe.Pointer, n uintptr) diff --git a/internal/monkey/common/page.go b/internal/monkey/common/page.go index fd72b13..044219e 100644 --- a/internal/monkey/common/page.go +++ b/internal/monkey/common/page.go @@ -18,7 +18,8 @@ package common import ( "syscall" - "unsafe" + + "github.com/bytedance/mockey/internal/tool" ) var pageSize = uintptr(syscall.Getpagesize()) @@ -32,12 +33,20 @@ func PageSize() int { } func AllocatePage() []byte { - var memStats uint64 = 0 - addr := sysAlloc(pageSize, &memStats) - return BytesOf(uintptr(addr), int(pageSize)) + page, err := allocate(int(pageSize)) + tool.Assert(err == nil, "allocate page failed: %v", err) + return page } func ReleasePage(mem []byte) { - memStats := uint64(cap(mem)) - sysFree(unsafe.Pointer(PtrOf(mem)), uintptr(cap(mem)), &memStats) + err := free(mem) + tool.Assert(err == nil, "free page failed: %v", err) +} + +func allocate(n int) ([]byte, error) { + return syscall.Mmap(-1, 0, int(n), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE) +} + +func free(b []byte) error { + return syscall.Munmap(b) } diff --git a/internal/monkey/common/runtime_above_1_21.go b/internal/monkey/common/runtime_link/stw/stw_1_21.go similarity index 69% rename from internal/monkey/common/runtime_above_1_21.go rename to internal/monkey/common/runtime_link/stw/stw_1_21.go index 78a0278..0a19eef 100644 --- a/internal/monkey/common/runtime_above_1_21.go +++ b/internal/monkey/common/runtime_link/stw/stw_1_21.go @@ -1,5 +1,5 @@ -//go:build go1.21 -// +build go1.21 +//go:build go1.21 && !go1.22 +// +build go1.21,!go1.22 /* * Copyright 2022 ByteDance Inc. @@ -17,19 +17,31 @@ * limitations under the License. */ -package common +package stw import ( _ "unsafe" ) -func StopTheWorld() { - const stwForTestResetDebugLog = 16 +func newSTWCtx() ctx { + return &stwCtx{} +} + +type stwCtx struct { +} + +const stwForTestResetDebugLog = 16 + +func (ctx *stwCtx) StopTheWorld() { stopTheWorld(stwForTestResetDebugLog) } +func (ctx *stwCtx) StartTheWorld() { + startTheWorld() +} + //go:linkname stopTheWorld runtime.stopTheWorld func stopTheWorld(reason uint8) -//go:linkname StartTheWorld runtime.startTheWorld -func StartTheWorld() +//go:linkname startTheWorld runtime.startTheWorld +func startTheWorld() diff --git a/internal/monkey/common/runtime_link/stw/stw_1_22.go b/internal/monkey/common/runtime_link/stw/stw_1_22.go new file mode 100644 index 0000000..447a6c0 --- /dev/null +++ b/internal/monkey/common/runtime_link/stw/stw_1_22.go @@ -0,0 +1,57 @@ +//go:build go1.22 && !go1.23 +// +build go1.22,!go1.23 + +/* + * Copyright 2022 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package stw + +import ( + _ "unsafe" +) + +func newSTWCtx() ctx { + return &stwCtx{} +} + +type stwCtx struct { + w worldStop +} + +const stwForTestResetDebugLog = 16 + +func (ctx *stwCtx) StopTheWorld() { + ctx.w = stopTheWorld(stwForTestResetDebugLog) +} +func (ctx *stwCtx) StartTheWorld() { + startTheWorld(ctx.w) +} + +// stwReason is an enumeration of reasons the world is stopping. +type stwReason uint8 + +// worldStop provides context from the stop-the-world required by the +// start-the-world. +type worldStop struct { + reason stwReason + start int64 +} + +//go:linkname stopTheWorld runtime.stopTheWorld +func stopTheWorld(reason stwReason) worldStop + +//go:linkname startTheWorld runtime.startTheWorld +func startTheWorld(w worldStop) diff --git a/internal/monkey/common/runtime_link/stw/stw_above_1_22.go b/internal/monkey/common/runtime_link/stw/stw_above_1_22.go new file mode 100644 index 0000000..d86c689 --- /dev/null +++ b/internal/monkey/common/runtime_link/stw/stw_above_1_22.go @@ -0,0 +1,59 @@ +//go:build go1.23 && mockey_stw +// +build go1.23,mockey_stw + +/* + * Copyright 2022 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package stw + +import ( + _ "unsafe" +) + +func newSTWCtx() ctx { + return &stwCtx{} +} + +type stwCtx struct { + w worldStop +} + +const stwForTestResetDebugLog = 16 + +func (ctx *stwCtx) StopTheWorld() { + ctx.w = stopTheWorld(stwForTestResetDebugLog) +} +func (ctx *stwCtx) StartTheWorld() { + startTheWorld(ctx.w) +} + +// stwReason is an enumeration of reasons the world is stopping. +type stwReason uint8 + +// worldStop provides context from the stop-the-world required by the +// start-the-world. +type worldStop struct { + reason stwReason + startedStopping int64 + finishedStopping int64 + stoppingCPUTime int64 +} + +//go:linkname stopTheWorld runtime.stopTheWorld +func stopTheWorld(reason stwReason) worldStop + +//go:linkname startTheWorld runtime.startTheWorld +func startTheWorld(w worldStop) diff --git a/internal/monkey/common/runtime_below_1_21.go b/internal/monkey/common/runtime_link/stw/stw_below_1_21.go similarity index 75% rename from internal/monkey/common/runtime_below_1_21.go rename to internal/monkey/common/runtime_link/stw/stw_below_1_21.go index fd6f2d7..5bf4c5f 100644 --- a/internal/monkey/common/runtime_below_1_21.go +++ b/internal/monkey/common/runtime_link/stw/stw_below_1_21.go @@ -17,18 +17,29 @@ * limitations under the License. */ -package common +package stw import ( _ "unsafe" ) -func StopTheWorld() { +func newSTWCtx() ctx { + return &stwCtx{} +} + +type stwCtx struct { +} + +func (ctx *stwCtx) StopTheWorld() { stopTheWorld("mockey") } +func (ctx *stwCtx) StartTheWorld() { + startTheWorld() +} + //go:linkname stopTheWorld runtime.stopTheWorld func stopTheWorld(reason string) -//go:linkname StartTheWorld runtime.startTheWorld -func StartTheWorld() +//go:linkname startTheWorld runtime.startTheWorld +func startTheWorld() diff --git a/internal/monkey/common/runtime_link/stw/stw_ctx.go b/internal/monkey/common/runtime_link/stw/stw_ctx.go new file mode 100644 index 0000000..11ccae5 --- /dev/null +++ b/internal/monkey/common/runtime_link/stw/stw_ctx.go @@ -0,0 +1,26 @@ +/* + * Copyright 2022 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package stw + +type ctx interface { + StopTheWorld() + StartTheWorld() +} + +func NewSTWCtx() ctx { + return newSTWCtx() +} diff --git a/internal/monkey/common/mem_below_1_19.go b/internal/monkey/common/runtime_link/stw/stw_disable.go similarity index 61% rename from internal/monkey/common/mem_below_1_19.go rename to internal/monkey/common/runtime_link/stw/stw_disable.go index 04625e7..4e1bf6b 100644 --- a/internal/monkey/common/mem_below_1_19.go +++ b/internal/monkey/common/runtime_link/stw/stw_disable.go @@ -1,5 +1,5 @@ -//go:build !go1.19 -// +build !go1.19 +//go:build go1.23 && !mockey_stw +// +build go1.23,!mockey_stw /* * Copyright 2022 ByteDance Inc. @@ -17,17 +17,21 @@ * limitations under the License. */ -package common +package stw -import "unsafe" +import ( + _ "unsafe" +) -/* - * This may lead to runtime.ReadMemStats inaccurate - * See https://github.com/bytedance/mockey/issues/13 - */ +func newSTWCtx() ctx { + return &stwCtx{} +} + +type stwCtx struct { +} -//go:linkname sysAlloc runtime.sysAlloc -func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer +func (ctx *stwCtx) StopTheWorld() { +} -//go:linkname sysFree runtime.sysFree -func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) +func (ctx *stwCtx) StartTheWorld() { +} diff --git a/internal/monkey/mem/write.go b/internal/monkey/mem/write.go index b924c47..5d71863 100644 --- a/internal/monkey/mem/write.go +++ b/internal/monkey/mem/write.go @@ -18,13 +18,15 @@ package mem import ( "github.com/bytedance/mockey/internal/monkey/common" + "github.com/bytedance/mockey/internal/monkey/common/runtime_link/stw" "github.com/bytedance/mockey/internal/tool" ) // WriteWithSTW copies data bytes to the target address and replaces the original bytes, during which it will stop the // world (only the current goroutine's P is running). func WriteWithSTW(target uintptr, data []byte) { - common.StopTheWorld() + ctx := stw.NewSTWCtx() + ctx.StopTheWorld() begin := target end := target + uintptr(len(data)) @@ -43,5 +45,5 @@ func WriteWithSTW(target uintptr, data []byte) { break } - common.StartTheWorld() + ctx.StartTheWorld() } diff --git a/internal/monkey/mem/write_darwin_test.go b/internal/monkey/mem/write_darwin_test.go deleted file mode 100644 index 76acb70..0000000 --- a/internal/monkey/mem/write_darwin_test.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2022 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mem - -import ( - "fmt" - "syscall" - "testing" - "unsafe" - - "github.com/bytedance/mockey/internal/monkey/common" - "github.com/smartystreets/goconvey/convey" -) - -func Test_write(t *testing.T) { - convey.Convey("Test_write", t, func() { - var a uint16 = 0x0 - var b uint32 = 0xffffffff - fmt.Printf("a=%x,b=%x\n", a, b) - arr := (*[4]byte)(unsafe.Pointer(&a)) - arr[2] = 0xa5 - fmt.Printf("a=%x,b=%x,aSlice=%x\n", a, b, arr) - target := uintptr(unsafe.Pointer(&a)) - data := uintptr(unsafe.Pointer(&b)) - res := write(target, data, 3, common.PageOf(target), common.PageSize(), syscall.PROT_READ|syscall.PROT_WRITE) - convey.So(res, convey.ShouldEqual, 0) - fmt.Printf("a=%x,b=%x,aSlice=%x\n", a, b, arr) - convey.So(a, convey.ShouldEqual, 0xffff) - convey.So(b, convey.ShouldEqual, 0xffffffff) - convey.So(*arr, convey.ShouldResemble, [4]byte{0xff, 0xff, 0xff, 0x00}) - }) -} diff --git a/internal/monkey/mem/write_linux_test.go b/internal/monkey/mem/write_linux_test.go deleted file mode 100644 index 76acb70..0000000 --- a/internal/monkey/mem/write_linux_test.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2022 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mem - -import ( - "fmt" - "syscall" - "testing" - "unsafe" - - "github.com/bytedance/mockey/internal/monkey/common" - "github.com/smartystreets/goconvey/convey" -) - -func Test_write(t *testing.T) { - convey.Convey("Test_write", t, func() { - var a uint16 = 0x0 - var b uint32 = 0xffffffff - fmt.Printf("a=%x,b=%x\n", a, b) - arr := (*[4]byte)(unsafe.Pointer(&a)) - arr[2] = 0xa5 - fmt.Printf("a=%x,b=%x,aSlice=%x\n", a, b, arr) - target := uintptr(unsafe.Pointer(&a)) - data := uintptr(unsafe.Pointer(&b)) - res := write(target, data, 3, common.PageOf(target), common.PageSize(), syscall.PROT_READ|syscall.PROT_WRITE) - convey.So(res, convey.ShouldEqual, 0) - fmt.Printf("a=%x,b=%x,aSlice=%x\n", a, b, arr) - convey.So(a, convey.ShouldEqual, 0xffff) - convey.So(b, convey.ShouldEqual, 0xffffffff) - convey.So(*arr, convey.ShouldResemble, [4]byte{0xff, 0xff, 0xff, 0x00}) - }) -} diff --git a/internal/monkey/mem/write_test.go b/internal/monkey/mem/write_test.go index 89640e3..2a64887 100644 --- a/internal/monkey/mem/write_test.go +++ b/internal/monkey/mem/write_test.go @@ -15,6 +15,7 @@ package mem // limitations under the License. import ( + "syscall" "testing" "github.com/bytedance/mockey/internal/monkey/common" @@ -59,3 +60,21 @@ func TestWrite(t *testing.T) { break } } + +func Test_write(t *testing.T) { + target := make([]byte, 1024) + for i := 0; i < len(target); i++ { + target[i] = 0x00 + } + data := make([]byte, 512) + for i := 0; i < len(data); i++ { + data[i] = 0xaa + byte(i) + } + + res := write(common.PtrOf(target), common.PtrOf(data), 3, common.PageOf(common.PtrOf(target)), common.PageSize(), syscall.PROT_READ|syscall.PROT_WRITE) + tool.Assert(res == 0, "assert fail") + tool.Assert(target[0] == 0xaa, "assert fail, target0 %x", target[0]) + tool.Assert(target[1] == 0xab, "assert fail, target1 %x", target[1]) + tool.Assert(target[2] == 0xac, "assert fail, target2 %x", target[2]) + tool.Assert(target[3] == 0x00, "assert fail, target3 %x", target[3]) +} diff --git a/internal/tool/goroutine.go b/internal/tool/goroutine.go index 8ec9400..9dd4a60 100644 --- a/internal/tool/goroutine.go +++ b/internal/tool/goroutine.go @@ -17,10 +17,25 @@ package tool import ( + "runtime" + "strconv" + "strings" "unsafe" ) -const gGoroutineIDOffset = 152 // Go1.10 +// The `goid` field offset in the struct g in the runtime package +// 152 before, 160 after go1.22 (go1.23 introduced the `syscallbp` field before goid) +var gGoroutineIDOffset uintptr + +func init() { + gGoroutineIDOffset = 152 // Go1.22- + vcode := strings.Split(strings.TrimPrefix(runtime.Version(), "go"), ".") + if len(vcode) >= 2 && vcode[0] == "1" { + if subv, err := strconv.ParseInt(vcode[1], 10, 64); err == nil && subv > 22 { + gGoroutineIDOffset = 160 // Go1.23+ + } + } +} func GetGoroutineID() int64 { g := getG()