go-kiss / monkey

Go语言猴子补丁框架
https://taoshu.in/go/monkey/
MIT License
119 stars 18 forks source link

在go版本>=1.20时generic mock失败 #12

Closed Ruomenger closed 11 months ago

Ruomenger commented 11 months ago

env

problem

使用go1.21.1和go1.20.8编译(编译命令禁止了内联和优化go build -gcflags '-N -l' -o generic-go1.21.1)运行examples中的generic时报错,使用go1.18.8和go1.19.8编译后执行可以正常输出结果为2。

error:

panic: Can not find CALL instruction

goroutine 1 [running]:
github.com/go-kiss/monkey.getFirstCallFunc(0x487be0)
        /home/ruo/go/src/monkey/monkey_amd64.go:118 +0x277
github.com/go-kiss/monkey.patchValue({0x48fd60?, 0x4a9cc8?, 0x1?}, {0x48fe20?, 0x4a9cd0?, 0x48f300?}, 0xc0000a6000)
        /home/ruo/go/src/monkey/monkey.go:145 +0x58a
github.com/go-kiss/monkey.Patch({0x48fd60?, 0x4a9cc8?}, {0x48fe20?, 0x4a9cd0?}, {0xc00004a6f0, 0x1, 0x7fb6b70ee5b8?})
        /home/ruo/go/src/monkey/monkey.go:54 +0x19e
main.main()
        /home/ruo/go/src/monkey/examples/generic/generic.go:26 +0xb2

generic.go代码

package main

import (
    "fmt"

    "github.com/go-kiss/monkey"
)

type S1[T int | float64] struct {
    i T
}

func (s *S1[T]) Get() T {
    return s.i
}

type S1__monkey__[T int | float64] struct {
    S1[T]
}

func (s *S1__monkey__[T]) Get() T {
    return s.i + 1
}

func main() {
    monkey.Patch((*S1[int]).Get, (*S1__monkey__[int]).Get, monkey.OptGeneric)
    s := S1[int]{i: 1}
    fmt.Println(s.Get()) // display 2
}

问题分析

明确了问题出现的场景及看了getFirstCallFunc的实现后,我用gdb对go1.18.8编译出的generic-go1.18.8和go1.21.1编译出的generic-go1.21.1进行了调试

go1.18.8反编译如下所示:

image

go1.21.1反编译如下所示: image

而在getFirstCallFunc中的判断逻辑是要求call指令的上一条指令使用RAX寄存器

if i.Op == x86asm.CALL && lastLea.Args[0].(x86asm.Reg) == x86asm.RAX

所以问题应该出现在这里,我将这里的判断逻辑修改为使用RAX或RBX寄存器后可以正常执行了,但是还不确定go1.20进行了什么改动导致的这一问题

taoso commented 11 months ago

最近工作上事比较多,可能没有时间搞这个事情 😭 欢迎 MR 😃

之前发现在 go 1.21 下 TestTimePatch 测试用例会 panic,也是没功夫处理。

go test -gcflags=-l -run TestTimePatch
Ruomenger commented 11 months ago

最近工作上事比较多,可能没有时间搞这个事情 😭 欢迎 MR 😃

之前发现在 go 1.21 下 TestTimePatch 测试用例会 panic,也是没功夫处理。

go test -gcflags=-l -run TestTimePatch

好的,我会抽时间看下这个问题(指的是mock失败这个问题,panic的这个问题有时间也会看下)。目前简单修改判断条件为RAX和RBX可以解决但是不确定是否能彻底解决这个问题

Ruomenger commented 11 months ago

最近工作上事比较多,可能没有时间搞这个事情 😭 欢迎 MR 😃

之前发现在 go 1.21 下 TestTimePatch 测试用例会 panic,也是没功夫处理。

go test -gcflags=-l -run TestTimePatch

这个TestTimePatch的问题是test时使用的参数不对所致的,应该使用下面的参数(其实-gcflags='all=-N'就足够了)禁止所有包的内联和优化即可

go test -gcflags='all=-N -l' -v -count=1 -run TestTimePatch

image

参考链接:

  1. Go gcflags/ldflags 的说明