Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gmon requires target binary to be built with go1.23+ #20

Merged
merged 4 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}