Skip to content

Commit 1d1afe8

Browse files
committed
Support Go 1.24
1 parent 86a052c commit 1d1afe8

File tree

10 files changed

+118
-34
lines changed

10 files changed

+118
-34
lines changed

.github/workflows/env/action.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,3 @@ runs:
2929
3030
sudo apt-get install -y llvm clang dwz curl unzip gcc-aarch64-linux-gnu \
3131
libc6-arm64-cross qemu-user-binfmt libc6:arm64
32-
- name: Set up Go
33-
uses: actions/setup-go@v5
34-
with:
35-
go-version: "1.23"
36-
check-latest: true
37-
cache-dependency-path: go.sum
38-
id: go

.github/workflows/unit-test-on-pull-request.yml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ jobs:
1515
uses: actions/checkout@v4
1616
- name: Set up environment
1717
uses: ./.github/workflows/env
18+
- name: Set up Go
19+
uses: actions/setup-go@v5
20+
with:
21+
go-version: "1.23"
22+
check-latest: true
23+
cache-dependency-path: go.sum
24+
id: go
1825
- name: Check for changes in licenses of dependencies
1926
run: |
2027
make legal
@@ -55,6 +62,13 @@ jobs:
5562
uses: actions/checkout@v4
5663
- name: Set up environment
5764
uses: ./.github/workflows/env
65+
- name: Set up Go
66+
uses: actions/setup-go@v5
67+
with:
68+
go-version: "1.23"
69+
check-latest: true
70+
cache-dependency-path: go.sum
71+
id: go
5872
- name: Get linter version
5973
id: linter-version
6074
run: (echo -n "version="; make linter-version) >> "$GITHUB_OUTPUT"
@@ -95,7 +109,15 @@ jobs:
95109
strategy:
96110
matrix:
97111
target_arch: [amd64, arm64]
112+
go_version: [1.23, 1.24]
98113
steps:
114+
- name: Set up Go
115+
uses: actions/setup-go@v5
116+
with:
117+
go-version: ${{ matrix.go_version }}
118+
check-latest: true
119+
cache-dependency-path: go.sum
120+
id: go
99121
- name: Clone code
100122
uses: actions/checkout@v4
101123
- name: Set up environment
@@ -105,7 +127,7 @@ jobs:
105127
- name: Upload integration test binaries
106128
uses: actions/upload-artifact@v4
107129
with:
108-
name: integration-test-binaries-${{ matrix.target_arch }}
130+
name: integration-test-binaries-${{ matrix.target_arch }}-${{ matrix.go_version}}
109131
path: support/*.test
110132

111133
integration-tests:
@@ -115,6 +137,7 @@ jobs:
115137
timeout-minutes: 10
116138
strategy:
117139
matrix:
140+
go_version: [1.23, 1.24]
118141
include:
119142
# List of available kernels here:
120143
# https://github.com/cilium/ci-kernels/pkgs/container/ci-kernels/versions?filters%5Bversion_type%5D=tagged
@@ -149,7 +172,7 @@ jobs:
149172
sudo mv ~/go/bin/bluebox /usr/local/bin/.
150173
- name: Fetch integration test binaries
151174
uses: actions/download-artifact@v4
152-
with: { name: "integration-test-binaries-${{ matrix.target_arch }}" }
175+
with: { name: "integration-test-binaries-${{ matrix.target_arch }}-${{ matrix.go_version}}" }
153176
- name: Fetch precompiled kernel
154177
run: |
155178
install -d ci-kernels

go_labels/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func randomString(n int) string {
2525
// to work with qemu/bluebox testing harness. A statically linked go test built binary doesn't
2626
// work with the go labels extractor ebpf program, not sure yet if this is a bug.
2727
func main() {
28-
labels := pprof.Labels("l1", randomString(16), "l2", randomString(16))
28+
labels := pprof.Labels("l1", randomString(16), "l2", randomString(16), "l3", randomString(16))
2929
lastUpdate := time.Now()
3030
pprof.Do(context.TODO(), labels, func(context.Context) {
3131
//nolint:revive

interpreter/golang/runtime_data.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,9 @@ var allOffsets = map[string]C.GoCustomLabelsOffsets{
113113
hmap_log2_bucket_count: 9,
114114
hmap_buckets: 16,
115115
},
116+
"go1.24": {
117+
m_offset: 48,
118+
curg: 192,
119+
labels: 352,
120+
},
116121
}

support/ebpf/go_labels.ebpf.c

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,35 +52,65 @@ bool process_bucket(PerCPURecord *record, void *label_buckets, int j) {
5252
return false;
5353
}
5454

55-
// Go processes store the current goroutine in thread local store. From there
56-
// this reads the g (aka goroutine) struct, then the m (the actual operating
57-
// system thread) of that goroutine, and finally curg (current goroutine). This
58-
// chain is necessary because getg().m.curg points to the current user g
59-
// assigned to the thread (curg == getg() when not on the system stack). curg
60-
// may be nil if there is no user g, such as when running in the scheduler. If
61-
// curg is nil, then g is either a system stack (called g0) or a signal handler
62-
// g (gsignal). Neither one will ever have label.
6355
static inline __attribute__((__always_inline__))
64-
bool get_go_custom_labels(struct pt_regs *ctx, PerCPURecord *record, GoCustomLabelsOffsets *offs) {
65-
long res;
56+
void process_slice_pair(PerCPURecord *record, struct GoSlice *labels_slice, int i) {
57+
CustomLabelsArray *out = &record->trace.custom_labels;
58+
if (out->len >= MAX_CUSTOM_LABELS)
59+
return;
6660

67-
size_t curg_ptr_addr;
68-
res = bpf_probe_read_user(&curg_ptr_addr, sizeof(void *), (void *)(record->customLabelsState.go_m_ptr + offs->curg));
61+
CustomLabel *lbl = &out->labels[out->len];
62+
void *str_addr = (char*)labels_slice->array + i * sizeof(struct GoString) * 2;
63+
long res = bpf_probe_read_user(&record->labels, sizeof(struct GoString)*2, str_addr);
6964
if (res < 0) {
70-
DEBUG_PRINT("cl: failed to read value for m_ptr->curg: %ld", res);
71-
return false;
65+
DEBUG_PRINT("cl: failed to read strings from labels slice (%lx): %ld", (unsigned long)str_addr, res);
66+
return;
67+
}
68+
unsigned klen = MIN(record->labels[0].len, CUSTOM_LABEL_MAX_KEY_LEN-1);
69+
res = bpf_probe_read_user(lbl->key, klen, record->labels[0].str);
70+
if (res) {
71+
DEBUG_PRINT("cl: failed to read key for custom label (%lx): %ld", (unsigned long) record->labels[0].str, res);
72+
return;
73+
}
74+
unsigned vlen = MIN(record->labels[1].len, CUSTOM_LABEL_MAX_VAL_LEN-1);
75+
res = bpf_probe_read_user(lbl->val, vlen, record->labels[1].str);
76+
if (res) {
77+
DEBUG_PRINT("cl: failed to read key for custom label (%lx): %ld", (unsigned long) record->labels[1].str, res);
78+
return;
7279
}
80+
out->len++;
81+
}
7382

74-
void *labels_map_ptr_ptr;
75-
res = bpf_probe_read_user(&labels_map_ptr_ptr, sizeof(void *), (void *)(curg_ptr_addr + offs->labels));
83+
static inline __attribute__((__always_inline__))
84+
bool get_go_custom_labels_from_slice(struct pt_regs *ctx, PerCPURecord *record, void *labels_slice_ptr) {
85+
//https://github.com/golang/go/blob/80e2e474/src/runtime/pprof/label.go#L20
86+
struct GoSlice labels_slice;
87+
long res = bpf_probe_read_user(&labels_slice, sizeof(struct GoSlice), labels_slice_ptr);
7688
if (res < 0) {
77-
DEBUG_PRINT("cl: failed to read value for curg->labels (%lx->%lx): %ld", (unsigned long)curg_ptr_addr,
78-
(unsigned long) offs->labels, res);
89+
DEBUG_PRINT("cl: failed to read value for labels slice (%lx): %ld", (unsigned long)labels_slice_ptr, res);
7990
return false;
8091
}
8192

93+
u64 label_count = MIN(MAX_CUSTOM_LABELS, labels_slice.len);
94+
switch (label_count) {
95+
case 10: process_slice_pair(record, &labels_slice, 9);
96+
case 9: process_slice_pair(record, &labels_slice, 8);
97+
case 8: process_slice_pair(record, &labels_slice, 7);
98+
case 7: process_slice_pair(record, &labels_slice, 6);
99+
case 6: process_slice_pair(record, &labels_slice, 5);
100+
case 5: process_slice_pair(record, &labels_slice, 4);
101+
case 4: process_slice_pair(record, &labels_slice, 3);
102+
case 3: process_slice_pair(record, &labels_slice, 2);
103+
case 2: process_slice_pair(record, &labels_slice, 1);
104+
case 1: process_slice_pair(record, &labels_slice, 0);
105+
}
106+
107+
return true;
108+
}
109+
110+
static inline __attribute__((__always_inline__))
111+
bool get_go_custom_labels_from_map(struct pt_regs *ctx, PerCPURecord *record, void *labels_map_ptr_ptr, GoCustomLabelsOffsets *offs) {
82112
void *labels_map_ptr;
83-
res = bpf_probe_read(&labels_map_ptr, sizeof(labels_map_ptr), labels_map_ptr_ptr);
113+
long res = bpf_probe_read(&labels_map_ptr, sizeof(labels_map_ptr), labels_map_ptr_ptr);
84114
if (res < 0) {
85115
DEBUG_PRINT("cl: failed to read value for labels_map_ptr (%lx): %ld", (unsigned long)labels_map_ptr_ptr, res);
86116
return false;
@@ -114,10 +144,6 @@ bool get_go_custom_labels(struct pt_regs *ctx, PerCPURecord *record, GoCustomLab
114144
// and we can't support as many buckets.
115145
u64 bucket_count = MIN(MAX_CUSTOM_LABELS, 1 << log_2_bucket_count);
116146
switch (bucket_count) {
117-
case 14: if (process_bucket(record, label_buckets, 13)) return true;
118-
case 13: if (process_bucket(record, label_buckets, 12)) return true;
119-
case 12: if (process_bucket(record, label_buckets, 11)) return true;
120-
case 11: if (process_bucket(record, label_buckets, 10)) return true;
121147
case 10: if (process_bucket(record, label_buckets, 9)) return true;
122148
case 9: if (process_bucket(record, label_buckets, 8)) return true;
123149
case 8: if (process_bucket(record, label_buckets, 7)) return true;
@@ -133,6 +159,41 @@ bool get_go_custom_labels(struct pt_regs *ctx, PerCPURecord *record, GoCustomLab
133159
return false;
134160
}
135161

162+
// Go processes store the current goroutine in thread local store. From there
163+
// this reads the g (aka goroutine) struct, then the m (the actual operating
164+
// system thread) of that goroutine, and finally curg (current goroutine). This
165+
// chain is necessary because getg().m.curg points to the current user g
166+
// assigned to the thread (curg == getg() when not on the system stack). curg
167+
// may be nil if there is no user g, such as when running in the scheduler. If
168+
// curg is nil, then g is either a system stack (called g0) or a signal handler
169+
// g (gsignal). Neither one will ever have label.
170+
static inline __attribute__((__always_inline__))
171+
bool get_go_custom_labels(struct pt_regs *ctx, PerCPURecord *record, GoCustomLabelsOffsets *offs) {
172+
long res;
173+
174+
size_t curg_ptr_addr;
175+
res = bpf_probe_read_user(&curg_ptr_addr, sizeof(void *), (void *)(record->customLabelsState.go_m_ptr + offs->curg));
176+
if (res < 0) {
177+
DEBUG_PRINT("cl: failed to read value for m_ptr->curg: %ld", res);
178+
return false;
179+
}
180+
181+
void *labels_ptr;
182+
res = bpf_probe_read_user(&labels_ptr, sizeof(void *), (void *)(curg_ptr_addr + offs->labels));
183+
if (res < 0) {
184+
DEBUG_PRINT("cl: failed to read value for curg->labels (%lx->%lx): %ld", (unsigned long)curg_ptr_addr,
185+
(unsigned long) offs->labels, res);
186+
return false;
187+
}
188+
189+
if (offs->hmap_buckets == 0) {
190+
// go 1.24+ labels is a slice
191+
return get_go_custom_labels_from_slice(ctx, record, labels_ptr);
192+
}
193+
194+
// go 1.23- labels is a map
195+
return get_go_custom_labels_from_map(ctx, record, labels_ptr, offs);
196+
}
136197

137198
SEC("perf_event/go_labels")
138199
int go_labels(struct pt_regs *ctx) {
29.5 KB
Binary file not shown.
29.6 KB
Binary file not shown.
5.24 KB
Binary file not shown.
5.23 KB
Binary file not shown.

support/ebpf/types.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ struct GoString {
797797

798798
struct GoSlice {
799799
void *array;
800-
s64 len;
800+
u64 len;
801801
s64 cap;
802802
};
803803

@@ -861,6 +861,8 @@ typedef struct PerCPURecord {
861861
NativeCustomLabel nativeCustomLabel;
862862
// Go labels scratch
863863
GoMapBucket goMapBucket;
864+
// Scratch for Go 1.24 labels
865+
struct GoString labels[2];
864866
};
865867
// Mask to indicate which unwinders are complete
866868
u32 unwindersDone;

0 commit comments

Comments
 (0)