diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2578f71..e5058dc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,4 +35,4 @@ jobs: - name: Unit Test run: MOCKEY_DEBUG=true go test -gcflags="all=-l -N" -covermode=atomic -coverprofile=coverage.out ./... - name: Benchmark - run: go test -bench=. -benchmem -run=none ./... + run: go test -gcflags="all=-l -N" -bench=. -benchmem -run=none ./... diff --git a/internal/monkey/mem/write_darwin.go b/internal/monkey/mem/write_darwin.go index 8867cec..5c80fae 100644 --- a/internal/monkey/mem/write_darwin.go +++ b/internal/monkey/mem/write_darwin.go @@ -28,7 +28,7 @@ import ( func Write(target uintptr, data []byte) error { targetPage := common.PageOf(target) fnPage := common.PageOf(reflect.ValueOf(write).Pointer()) - tool.DebugPrintf("Write: target page(0x%x), fn page(0x%x)\n", targetPage, fnPage) + tool.DebugPrintf("Write: target page(0x%x), fn page(0x%x), len(%v)\n", targetPage, fnPage, len(data)) res := write(target, common.PtrOf(data), len(data), targetPage, common.PageSize(), syscall.PROT_READ|syscall.PROT_EXEC) if res != 0 { return fmt.Errorf("write failed, code %v", res) diff --git a/internal/monkey/mem/write_linux.go b/internal/monkey/mem/write_linux.go index 4a85c04..4f60687 100644 --- a/internal/monkey/mem/write_linux.go +++ b/internal/monkey/mem/write_linux.go @@ -28,7 +28,7 @@ import ( func Write(target uintptr, data []byte) error { targetPage := common.PageOf(target) fnPage := common.PageOf(reflect.ValueOf(write).Pointer()) - tool.DebugPrintf("Write: target page(0x%x), fn page(0x%x)\n", targetPage, fnPage) + tool.DebugPrintf("Write: target page(0x%x), fn page(0x%x), len(%v)\n", targetPage, fnPage, len(data)) res := write(target, common.PtrOf(data), len(data), targetPage, common.PageSize(), syscall.PROT_READ|syscall.PROT_EXEC) if res != 0 { return fmt.Errorf("write failed, code %v", res) diff --git a/internal/tool/check_gcflags.go b/internal/tool/check_gcflags.go new file mode 100644 index 0000000..83a9a8d --- /dev/null +++ b/internal/tool/check_gcflags.go @@ -0,0 +1,36 @@ +/* + * 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 tool + +import ( + "os" +) + +func init() { + // set MOCKEY_CHECK_GCFLAGS=false to disable this check + checkGCflags() +} + +func checkGCflags() int { + if flag := os.Getenv("MOCKEY_CHECK_GCFLAGS"); flag != "false" && !IsGCFlagsSet() { + panic(` +Mockey init failed, did you forget to add -gcflags="all=-N -l" ? +(Set env MOCKEY_CHECK_GCFLAGS=false to disable gcflags check) + `) + } + return 0 +} diff --git a/internal/tool/check_gcflags_amd64.go b/internal/tool/check_gcflags_amd64.go new file mode 100644 index 0000000..01031c8 --- /dev/null +++ b/internal/tool/check_gcflags_amd64.go @@ -0,0 +1,54 @@ +/* + * 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 tool + +import ( + "reflect" + "unsafe" + + "golang.org/x/arch/x86/x86asm" +) + +func fn() { +} + +func fn2() { + fn() +} + +func IsGCFlagsSet() bool { + var asm []byte + header := (*reflect.SliceHeader)(unsafe.Pointer(&asm)) + header.Data = reflect.ValueOf(fn2).Pointer() + header.Len = 1000 + header.Cap = 1000 + + flag := false + pos := 0 + for pos < len(asm) { + inst, _ := x86asm.Decode(asm[pos:], 64) + if inst.Op == x86asm.RET { + break + } + if inst.Op == x86asm.CALL { + flag = true + break + } + pos += int(inst.Len) + } + return flag +} diff --git a/internal/tool/check_gcflags_arm64.go b/internal/tool/check_gcflags_arm64.go new file mode 100644 index 0000000..b38be9b --- /dev/null +++ b/internal/tool/check_gcflags_arm64.go @@ -0,0 +1,54 @@ +/* + * 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 tool + +import ( + "reflect" + "unsafe" + + "golang.org/x/arch/arm64/arm64asm" +) + +func fn() { +} + +func fn2() { + fn() +} + +func IsGCFlagsSet() bool { + var asm []byte + header := (*reflect.SliceHeader)(unsafe.Pointer(&asm)) + header.Data = reflect.ValueOf(fn2).Pointer() + header.Len = 1000 + header.Cap = 1000 + + flag := false + pos := 0 + for pos < len(asm) { + inst, _ := arm64asm.Decode(asm[pos:]) + if inst.Op == arm64asm.RET { + break + } + if inst.Op == arm64asm.BL { + flag = true + break + } + pos += int(unsafe.Sizeof(inst.Enc)) + } + return flag +}