Skip to content

Commit

Permalink
gmon requires target binary to be built with go1.23+ (#20)
Browse files Browse the repository at this point in the history
* Fix offset of goid

Signed-off-by: keisku <[email protected]>

* Bump Go version

Signed-off-by: keisku <[email protected]>

* Clarify Go version requirement of target binary

Signed-off-by: keisku <[email protected]>

* return error if go version of target is less than 1.23

Signed-off-by: keisku <[email protected]>

---------

Signed-off-by: keisku <[email protected]>
  • Loading branch information
keisku authored Sep 16, 2024
1 parent 3992ae0 commit 56bb66a
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions ebpf/c/gmon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
3 changes: 2 additions & 1 deletion ebpf/c/goroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion fixture/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module fixture

go 1.22.1
go 1.23.1
2 changes: 1 addition & 1 deletion gmon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand Down
28 changes: 28 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bufio"
"context"
"debug/buildinfo"
"errors"
"flag"
"fmt"
Expand All @@ -16,6 +17,7 @@ import (
"runtime"
"runtime/debug"
"runtime/trace"
"strconv"
"strings"

"github.com/cilium/ebpf/rlimit"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

0 comments on commit 56bb66a

Please sign in to comment.