agiledragon / gomonkey

gomonkey is a library to make monkey patching in unit tests easy
MIT License
1.92k stars 178 forks source link

signal SIGSEGV: segmentation violation #132

Closed limpo1989 closed 1 year ago

limpo1989 commented 1 year ago

使用gomonkey配合plugin,实现一个简单的热修复功能会发生崩溃,刚开始patching之后一切正常,运行一会以后就会发生错误

main.go

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "plugin"
    "reflect"
    "sync/atomic"
    "time"

    gomonkey "github.com/agiledragon/gomonkey/v2"
    "gocase/gopatch/route"
)

var HotfixVersion = "main"

func IndexHandler() func(http.ResponseWriter, *http.Request) {
    //return route.Index
    var counter int32
    return func(writer http.ResponseWriter, request *http.Request) {
        fmt.Fprintln(writer, "plugin handle:", atomic.AddInt32(&counter, 1))
    }
}

func main() {
    http.HandleFunc("/", route.Index)

    // 热修复 route.Index 逻辑
    {
        p, err := plugin.Open("http_v1.so")
        if nil != err {
            panic(err)
        }

        sym, err := p.Lookup("IndexHandler")
        if nil != err {
            panic(err)
        }

        fn := sym.(func() func(http.ResponseWriter, *http.Request))
        handler := fn()

        fmt.Println("patching...", reflect.ValueOf(route.Index).Pointer(), " => ", reflect.ValueOf(handler).Pointer())

        gomonkey.ApplyFunc(route.Index, handler)
    }

    // 等3s后开始启动客户端访问接口
    time.AfterFunc(time.Second*3, func() {

        fmt.Println("start client...")
        for i := 0; i < 3; i++ {
            go startClient()
        }
    })

    // 启动http服务器
    fmt.Println("http server listen...")
    err := http.ListenAndServe(":8080", nil)
    fmt.Println("http server listen failed: ", err)
}

func startClient() {
    for {
        resp, err := http.Get("http://127.0.0.1:8080/")
        if nil != err {
            fmt.Println("http.Get: ", err)
            return
        }
        io.Copy(os.Stdout, resp.Body)
        resp.Body.Close()
    }
}

route.go

var counter int32
func Index(writer http.ResponseWriter, request *http.Request) {
    fmt.Fprintln(writer, "main: hello gopatch/", atomic.AddInt32(&counter, 1))
}

run_http.sh

#!/bin/bash

set -e

echo "build main program..."
go build -gcflags=all=-l -ldflags="-X main.HotfixVersion=main" -o http_main main.go

echo "please modify v1 plugin, press enter key to continue..."
read input

echo "build plugin v1..."
go build -gcflags=all=-l -buildmode=plugin -ldflags="-X main.HotfixVersion=v1" -o http_v1.so main.go

echo "run main program..."
./http_main

输出结果

patching... 8606880  =>  139904623738208
http server listen...
start client...
plugin handle: 1
plugin handle: 3
plugin handle: 2
plugin handle: 4
plugin handle: 5
plugin handle: 6
plugin handle: 7
plugin handle: 9
plugin handle: 8
plugin handle: 10
plugin handle: 11
plugin handle: 13
plugin handle: 12
plugin handle: 14
plugin handle: 15
plugin handle: 16
plugin handle: 17
plugin handle: 18
plugin handle: 19
plugin handle: 20
plugin handle: 21
plugin handle: 22
plugin handle: 23
plugin handle: 25
plugin handle: 24
plugin handle: 26
plugin handle: 27
plugin handle: 29
plugin handle: 28
plugin handle: 31
plugin handle: 32
plugin handle: 33
plugin handle: 30
plugin handle: 34
plugin handle: 35
plugin handle: 36
plugin handle: 38
plugin handle: 37
plugin handle: 39
plugin handle: 41
unexpected fault address 0x8dffe8
fatal error: fault
plugin handle: 40
[signal SIGSEGV: segmentation violation code=0x2 addr=0x8dffe8 pc=0x8dffe8]

goroutine 179 [running]:
runtime.throw({0x8d9d5a?, 0xc000016000?})
        /usr/local/go/src/runtime/panic.go:1047 +0x5d fp=0xc000206a70 sp=0xc000206a40 pc=0x58e6dd
runtime.sigpanic()
        /usr/local/go/src/runtime/signal_unix.go:851 +0x1e5 fp=0xc000206aa0 sp=0xc000206a70 pc=0x5a5465
net/http.HandlerFunc.ServeHTTP(0x686a65?, {0x929960?, 0xc000344000?}, 0x922938?)
        /usr/local/go/src/net/http/server.go:2123 +0x2f fp=0xc000206ac8 sp=0xc000206aa0 pc=0x80444f
net/http.(*ServeMux).ServeHTTP(0x0?, {0x929960, 0xc000344000}, 0xc0001ce000)
        /usr/local/go/src/net/http/server.go:2500 +0xc2 fp=0xc000206b00 sp=0xc000206ac8 pc=0x8059a2
net/http.serverHandler.ServeHTTP({0xc00020b3b0?}, {0x929960?, 0xc000344000?}, 0xc0001ce000?)
        /usr/local/go/src/net/http/server.go:2936 +0x23c fp=0xc000206b98 sp=0xc000206b00 pc=0x806fdc
net/http.(*conn).serve(0xc000208f30, {0x929d68, 0xc00011af00})
        /usr/local/go/src/net/http/server.go:1995 +0xad0 fp=0xc000206fb8 sp=0xc000206b98 pc=0x8035b0
net/http.(*Server).Serve.func3()
        /usr/local/go/src/net/http/server.go:3089 +0x2e fp=0xc000206fe0 sp=0xc000206fb8 pc=0x80792e
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc000206fe8 sp=0xc000206fe0 pc=0x5c2861
created by net/http.(*Server).Serve
        /usr/local/go/src/net/http/server.go:3089 +0x45f
...
limpo1989 commented 1 year ago

测试环境:

limpo1989 commented 1 year ago

切换为 gohook 后运行正常了,不会崩溃了