diff --git a/README.md b/README.md index 7bc554d..eafeebf 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - amd64 (x86_64) - Linux Kernel 5.8+ since `gmon` uses [BPF ring buffer](https://nakryiko.com/posts/bpf-ringbuf/) +- Target Go binary must be compiled with Go 1.23+ since `gmon` uses fixed offset to get goroutine ID # Usage diff --git a/ebpf/c/gmon.c b/ebpf/c/gmon.c index 7f68569..30373cd 100644 --- a/ebpf/c/gmon.c +++ b/ebpf/c/gmon.c @@ -24,9 +24,9 @@ int runtime_newproc1(struct pt_regs *ctx) { bpf_printk("%s:%d | failed to extract new goroutine pointer from retval\n", __FILE__, __LINE__); return 0; } - // `pahole -C runtime.g /path/to/gobinary 2>/dev/null` shows the offsets of the goid which is 152. + // `pahole -C runtime.g /path/to/gobinary 2>/dev/null` shows the offsets of the goid. int64_t goid = 0; - if (bpf_core_read_user(&goid, sizeof(int64_t), newg_p + 152)) { + if (bpf_core_read_user(&goid, sizeof(int64_t), newg_p + 160)) { bpf_printk("%s:%d | failed to read goroutine id from newg with the offset\n", __FILE__, __LINE__); return 0; } diff --git a/ebpf/c/goroutine.h b/ebpf/c/goroutine.h index 8ba80c0..4f7a271 100644 --- a/ebpf/c/goroutine.h +++ b/ebpf/c/goroutine.h @@ -18,7 +18,7 @@ struct gobuf_t { uintptr_t bp; }; -// https://github.com/golang/go/blob/release-branch.go1.21/src/runtime/runtime2.go#L447 +// https://github.com/golang/go/blob/release-branch.go1.23/src/runtime/runtime2.go#L458 struct g_t { struct stack_t stack_instance; uintptr_t stackguard0; @@ -29,6 +29,7 @@ struct g_t { struct gobuf_t sched; uintptr_t syscallsp; uintptr_t syscallpc; + uintptr_t syscallbp; uintptr_t stktopsp; uintptr_t param; uint32_t atomicstatus; diff --git a/fixture/go.mod b/fixture/go.mod index 49a1fda..828d4a1 100644 --- a/fixture/go.mod +++ b/fixture/go.mod @@ -1,3 +1,3 @@ module fixture -go 1.22.1 +go 1.23.1 diff --git a/gmon.sh b/gmon.sh index 8d1b6fd..f1df66e 100755 --- a/gmon.sh +++ b/gmon.sh @@ -43,7 +43,7 @@ ln -s /usr/bin/llvm-strip-14 /usr/bin/llvm-strip ln -s /usr/bin/clang-14 /usr/bin/clang ln -s /usr/bin/clang-format-14 /usr/bin/clang-format wget -O- --no-check-certificate https://github.com/libbpf/bpftool/releases/download/v7.3.0/bpftool-v7.3.0-amd64.tar.gz | tar -xzf - -C /usr/bin && chmod +x /usr/bin/bpftool -wget -O- --no-check-certificate https://go.dev/dl/go1.22.3.linux-amd64.tar.gz | tar -xzf - -C /usr/local && chmod +x /usr/local/go/bin/go && ln -s /usr/local/go/bin/go /usr/bin/go +wget -O- --no-check-certificate https://go.dev/dl/go1.23.1.linux-amd64.tar.gz | tar -xzf - -C /usr/local && chmod +x /usr/local/go/bin/go && ln -s /usr/local/go/bin/go /usr/bin/go END WORKDIR /usr/src COPY go.mod go.mod diff --git a/go.mod b/go.mod index 419c9f9..08102e1 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/keisku/gmon -go 1.22.3 +go 1.23.1 require ( github.com/cilium/ebpf v0.15.0 diff --git a/main.go b/main.go index ee523ef..963c1dd 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "bufio" "context" + "debug/buildinfo" "errors" "flag" "fmt" @@ -16,6 +17,7 @@ import ( "runtime" "runtime/debug" "runtime/trace" + "strconv" "strings" "github.com/cilium/ebpf/rlimit" @@ -87,6 +89,14 @@ func main() { errlog.Fatalln("gmon only works on amd64 Linux") } + binfo, err := buildinfo.ReadFile(*binPath) + if err != nil { + errlog.Fatalln(err) + } + if !isGoVersion123OrHigher(binfo.GoVersion) { + errlog.Fatalf("gmon requires Go 1.23 or higher, but %s is used for %s", binfo.GoVersion, binfo.Main.Path) + } + if *traceOutPath != "" { traceOutFile, err := os.Create(*traceOutPath) if err != nil { @@ -161,3 +171,21 @@ func logTracePipe(done <-chan struct{}) { }() <-done } + +func isGoVersion123OrHigher(v string) bool { + versionSplit := strings.Split(v, ".") + if len(versionSplit) != 3 { + return false + } + if versionSplit[0] != "go1" { + return false + } + minor, err := strconv.Atoi(versionSplit[1]) + if err != nil { + return false + } + if minor < 23 { + return false + } + return true +}