Skip to content

Commit 01ef8e4

Browse files
authored
debuginfo: Add pure Go ELF Writer to extract debug information (parca-dev#454)
* Add pure Go ELF Writer to extract debug information Signed-off-by: Kemal Akkoyun <[email protected]> * Fix linter errors Signed-off-by: Kemal Akkoyun <[email protected]> * Add tests for symbolization Signed-off-by: Kemal Akkoyun <[email protected]> * Add a way to only keep section headers Signed-off-by: Kemal Akkoyun <[email protected]> * Fix symbolization issues Signed-off-by: Kemal Akkoyun <[email protected]>
1 parent b15e64d commit 01ef8e4

25 files changed

+1234
-237
lines changed

.github/workflows/build.yml

+10-4
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ jobs:
9696
cd bpf && cargo check
9797
9898
- name: Install golangci-lint
99-
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.45.0
99+
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.46.2
100100

101101
- name: Install gofumpt
102102
run: go install mvdan.cc/gofumpt@latest
@@ -120,16 +120,22 @@ jobs:
120120
restore-keys: |
121121
${{ runner.os }}-go-
122122
123+
- name: Build BPF
124+
run: make bpf
125+
126+
- name: Build
127+
run: make build
128+
123129
- name: Test
124-
run: make bpf test vet lint
130+
run: make test
125131

126132
- name: Format
127133
run: |
128134
make format
129135
git diff --exit-code
130136
131-
- name: Build
132-
run: make build
137+
- name: Vet
138+
run: make vet lint
133139

134140
- name: Archive generatated artifacts
135141
uses: actions/upload-artifact@v3

.golangci.yml

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ linters:
1717
- unused
1818
disable:
1919
- exhaustivestruct
20+
- exhaustruct
2021
- funlen
2122
- gci
2223
- gochecknoglobals
@@ -42,6 +43,10 @@ issues:
4243
- path: _test.go
4344
linters:
4445
- errcheck
46+
- path: pkg/elfwriter
47+
linters:
48+
- dupl
49+
4550

4651
linters-settings:
4752
depguard:

Dockerfile

+1-16
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-14 main\n" >
2020

2121
# NOTICE: -o Acquire::Check-Valid-Until="false" added as a mitigation, see https://github.com/parca-dev/parca-agent/issues/10 for further details.
2222
RUN apt-get -o Acquire::Check-Valid-Until="false" update -y && \
23-
apt-get install --no-install-recommends -yq llvm-14-dev libclang-14-dev clang-14 make gcc coreutils elfutils binutils zlib1g-dev libelf-dev ca-certificates netbase && \
23+
apt-get install --no-install-recommends -yq llvm-14-dev libclang-14-dev clang-14 make gcc coreutils zlib1g-dev libelf-dev ca-certificates netbase && \
2424
ln -s /usr/bin/clang-14 /usr/bin/clang && \
2525
ln -s /usr/bin/llc-14 /usr/bin/llc
2626

@@ -52,24 +52,9 @@ RUN make build
5252

5353
FROM ${DEBIAN_BASE} as all
5454

55-
ARG LINUX_ARCH
56-
ENV LINUX_ARCH=$LINUX_ARCH
57-
58-
# Dependencies for objcopy and eu-strip.
59-
COPY --from=build /lib/${LINUX_ARCH}-linux-gnu/libpthread.so.0 /lib/${LINUX_ARCH}-linux-gnu/libpthread.so.0
60-
COPY --from=build /usr/lib/${LINUX_ARCH}-linux-gnu/libelf-0.183.so /usr/lib/${LINUX_ARCH}-linux-gnu/libelf-0.183.so
61-
COPY --from=build /usr/lib/${LINUX_ARCH}-linux-gnu/libdw.so.1 /usr/lib/${LINUX_ARCH}-linux-gnu/libdw.so.1
62-
RUN ln -s /usr/lib/${LINUX_ARCH}-linux-gnu/libelf-0.183.so /usr/lib/${LINUX_ARCH}-linux-gnu/libelf.so.1
63-
COPY --from=build /lib/${LINUX_ARCH}-linux-gnu/libz.so.1 /lib/${LINUX_ARCH}-linux-gnu/libz.so.1
64-
COPY --from=build /lib/${LINUX_ARCH}-linux-gnu/libc.so.6 /lib/${LINUX_ARCH}-linux-gnu/libc.so.6
65-
COPY --from=build /usr/lib/${LINUX_ARCH}-linux-gnu/libbfd-2.35.2-system.so /usr/lib/${LINUX_ARCH}-linux-gnu/libbfd-2.35.2-system.so
66-
COPY --from=build /lib/${LINUX_ARCH}-linux-gnu/libdl.so.2 /lib/${LINUX_ARCH}-linux-gnu/libdl.so.2
67-
6855
COPY --from=build /etc/nsswitch.conf /etc/nsswitch.conf
6956
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
7057
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
71-
COPY --from=build /usr/bin/objcopy /usr/bin/objcopy
72-
COPY --from=build /usr/bin/eu-strip /usr/bin/eu-strip
7358
COPY --from=build /parca-agent/dist/parca-agent /bin/parca-agent
7459

7560
FROM scratch

Dockerfile.dev

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.18.3-bullseye
1+
FROM golang:1.18.3-bullseye as build
22

33
# tag=1.24.3
44
ARG RUSTUP_VERSION=ce5817a94ac372804babe32626ba7fd2d5e1b6ac
@@ -15,7 +15,7 @@ RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
1515
RUN echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-14 main\n" > /etc/apt/sources.list.d/llvm.list
1616

1717
RUN apt-get -o Acquire::Check-Valid-Until="false" update -y && \
18-
apt-get install --no-install-recommends -yq llvm-14-dev libclang-14-dev clang-14 make gcc coreutils elfutils binutils zlib1g-dev libelf-dev ca-certificates netbase && \
18+
apt-get install --no-install-recommends -yq llvm-14-dev libclang-14-dev clang-14 make gcc coreutils zlib1g-dev libelf-dev ca-certificates netbase && \
1919
ln -s /usr/bin/clang-14 /usr/bin/clang && \
2020
ln -s /usr/bin/llc-14 /usr/bin/llc
2121

@@ -46,8 +46,6 @@ RUN make build
4646

4747
FROM debian:bullseye-slim as all
4848

49-
RUN apt-get update -y && apt-get install --no-install-recommends -y elfutils binutils
50-
5149
COPY --from=build /etc/nsswitch.conf /etc/nsswitch.conf
5250
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
5351
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo

Makefile

+4-7
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@ else
2727
ARCH ?= arm64
2828
endif
2929

30-
ifeq ($(ARCH), amd64)
31-
LINUX_ARCH ?= x86_64=x86
32-
else
33-
LINUX_ARCH ?= aarch64=arm64
34-
endif
35-
3630
KERN_RELEASE ?= $(shell uname -r)
3731
KERN_BLD_PATH ?= $(if $(KERN_HEADERS),$(KERN_HEADERS),/lib/modules/$(KERN_RELEASE)/build)
3832
KERN_SRC_PATH ?= $(if $(KERN_HEADERS),$(KERN_HEADERS),$(if $(wildcard /lib/modules/$(KERN_RELEASE)/source),/lib/modules/$(KERN_RELEASE)/source,$(KERN_BLD_PATH)))
@@ -131,7 +125,6 @@ libbpf: $(LIBBPF_HEADERS) $(LIBBPF_OBJ)
131125
bpf: $(OUT_BPF)
132126

133127
ifndef DOCKER
134-
.PHONY: $(OUT_BPF)
135128
$(OUT_BPF): $(BPF_SRC) | $(OUT_DIR)
136129
mkdir -p $(OUT_BPF_DIR)
137130
$(MAKE) -C bpf build
@@ -197,6 +190,10 @@ check_%:
197190
container:
198191
./make-containers.sh $(OUT_DOCKER):$(VERSION)
199192

193+
.PHONY: container-dev
194+
container-dev:
195+
docker build -t parca-dev/parca-agent:dev --build-arg=GOLANG_BASE=golang:1.18.3-bullseye --build-arg=DEBIAN_BASE=debian:bullseye-slim .
196+
200197
.PHONY: sign-container
201198
sign-container:
202199
crane digest $(OUT_DOCKER):$(VERSION)

Tiltfile

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ docker_build(
1414
'./go.sum',
1515
'./internal',
1616
'./pkg',
17+
'./rust-toolchain.toml'
1718
],
1819
)
1920
k8s_yaml('deploy/tilt/parca-agent-daemonSet.yaml')

cmd/debug-info/main.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,7 @@ func run() error {
157157

158158
for _, f := range files {
159159
// ./out/<buildid>/debuginfo
160-
_, p := filepath.Split(filepath.Dir(f))
161-
output := filepath.Join(flags.Extract.OutputDir, filepath.Base(p))
160+
output := filepath.Join(flags.Extract.OutputDir, filepath.Base(f))
162161
if err := os.Rename(f, output); err != nil {
163162
level.Error(logger).Log("msg", "failed to move file", "file", output, "err", err)
164163
continue
@@ -171,11 +170,15 @@ func run() error {
171170
})
172171
case "buildid <path>":
173172
g.Add(func() error {
174-
_, err := buildid.BuildID(flags.Buildid.Path)
173+
id, err := buildid.BuildID(flags.Buildid.Path)
175174
if err != nil {
176175
level.Error(logger).Log("msg", "failed to extract elf build ID", "err", err)
177176
return err
178177
}
178+
if id == "" {
179+
return errors.New("failed to extract ELF build ID")
180+
}
181+
fmt.Fprintf(os.Stdout, "Build ID: %s\n", id)
179182
return nil
180183
}, func(error) {
181184
cancel()

cmd/parca-agent/main.go

-26
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"net/http/pprof"
2626
"net/url"
2727
"os"
28-
"os/exec"
2928
"os/signal"
3029
"sort"
3130
"strings"
@@ -159,31 +158,6 @@ func main() {
159158
if !flags.DebugInfoDisable {
160159
level.Info(logger).Log("msg", "debug information collection is enabled")
161160
debugInfoClient = parcadebuginfo.NewDebugInfoClient(conn)
162-
163-
// Check if external dependencies for debug info extraction is there and healthy.
164-
for _, c := range [2]string{"objcopy", "eu-strip"} {
165-
if _, err := exec.LookPath(c); err != nil {
166-
if errors.Is(err, exec.ErrNotFound) {
167-
level.Error(logger).Log(
168-
"msg", "failed to find external dependency in the PATH; make sure it is installed and added to the PATH",
169-
"cmd", c,
170-
)
171-
os.Exit(1)
172-
}
173-
}
174-
175-
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
176-
if out, err := exec.CommandContext(ctx, c, "--help").CombinedOutput(); err != nil {
177-
cancel()
178-
level.Error(logger).Log(
179-
"msg", "failed to check whether external dependency is healthy",
180-
"err", err,
181-
"cmd", c,
182-
"output", string(out),
183-
)
184-
os.Exit(1)
185-
}
186-
}
187161
}
188162
}
189163

deploy/tilt/parca-server-deployment.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
app.kubernetes.io/component: observability
66
app.kubernetes.io/instance: parca
77
app.kubernetes.io/name: parca
8-
app.kubernetes.io/version: v0.11.0
8+
app.kubernetes.io/version: v0.11.1
99
name: parca
1010
namespace: parca
1111
spec:
@@ -21,15 +21,15 @@ spec:
2121
app.kubernetes.io/component: observability
2222
app.kubernetes.io/instance: parca
2323
app.kubernetes.io/name: parca
24-
app.kubernetes.io/version: v0.11.0
24+
app.kubernetes.io/version: v0.11.1
2525
spec:
2626
containers:
2727
- args:
2828
- /parca
2929
- --config-path=/var/parca/parca.yaml
3030
- --log-level=info
3131
- --cors-allowed-origins=*
32-
image: ghcr.io/parca-dev/parca:v0.11.0
32+
image: ghcr.io/parca-dev/parca:v0.11.1
3333
livenessProbe:
3434
exec:
3535
command:

deploy/tilt/parca-server-role.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
app.kubernetes.io/component: observability
66
app.kubernetes.io/instance: parca
77
app.kubernetes.io/name: parca
8-
app.kubernetes.io/version: v0.11.0
8+
app.kubernetes.io/version: v0.11.1
99
name: parca
1010
namespace: parca
1111
rules:

deploy/tilt/parca-server-roleBinding.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
app.kubernetes.io/component: observability
66
app.kubernetes.io/instance: parca
77
app.kubernetes.io/name: parca
8-
app.kubernetes.io/version: v0.11.0
8+
app.kubernetes.io/version: v0.11.1
99
name: parca
1010
namespace: parca
1111
roleRef:

deploy/tilt/parca-server-service.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
app.kubernetes.io/component: observability
66
app.kubernetes.io/instance: parca
77
app.kubernetes.io/name: parca
8-
app.kubernetes.io/version: v0.11.0
8+
app.kubernetes.io/version: v0.11.1
99
name: parca
1010
namespace: parca
1111
spec:

deploy/tilt/parca-server-serviceAccount.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ metadata:
55
app.kubernetes.io/component: observability
66
app.kubernetes.io/instance: parca
77
app.kubernetes.io/name: parca
8-
app.kubernetes.io/version: v0.11.0
8+
app.kubernetes.io/version: v0.11.1
99
name: parca
1010
namespace: parca

env.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ go install github.com/campoy/embedmd@latest
2727

2828
go install mvdan.cc/gofumpt@latest
2929

30-
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.0
30+
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require (
2525
github.com/prometheus/common v0.34.0
2626
github.com/prometheus/prometheus v1.8.2-0.20220315145411-881111fec433
2727
github.com/stretchr/testify v1.7.2
28-
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
28+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
2929
google.golang.org/grpc v1.47.0
3030
k8s.io/api v0.24.1
3131
k8s.io/apimachinery v0.24.1

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1626,8 +1626,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
16261626
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
16271627
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
16281628
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1629-
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
1630-
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1629+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
1630+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
16311631
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
16321632
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
16331633
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

make-containers.sh

+1-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ set +euxo pipefail
33

44
MANIFEST="$1"
55
ARCHS=('arm64' 'amd64')
6-
LINUX_ARCHS=('aarch64' 'x86_64')
76

87
# podman manifest inspect docker.io/golang:1.18.1-bullseye
98
# SHA order is respectively arm64, amd64
@@ -21,15 +20,13 @@ DOCKER_DEBIAN_SHAS=(
2120

2221
for i in "${!ARCHS[@]}"; do
2322
ARCH=${ARCHS[$i]}
24-
LINUX_ARCH=${LINUX_ARCHS[$i]}
2523
DOCKER_GOLANG_SHA=${DOCKER_GOLANG_SHAS[$i]}
2624
DOCKER_DEBIAN_SHA=${DOCKER_DEBIAN_SHAS[$i]}
27-
echo "Building manifest for $MANIFEST with arch \"$ARCH\" which is linux-arch \"$LINUX_ARCH\""
25+
echo "Building manifest for $MANIFEST with arch \"$ARCH\""
2826
podman build \
2927
--build-arg GOLANG_BASE=$DOCKER_GOLANG_SHA \
3028
--build-arg DEBIAN_BASE=$DOCKER_DEBIAN_SHA \
3129
--build-arg ARCH=$ARCH \
32-
--build-arg LINUX_ARCH=$LINUX_ARCH \
3330
--arch $ARCH \
3431
--timestamp 0 \
3532
--manifest $MANIFEST .; \

pkg/buildid/buildid.go

+27-4
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,22 @@ func BuildID(path string) (string, error) {
3232
return "", fmt.Errorf("failed to open elf: %w", err)
3333
}
3434

35-
hasBuildIDSection := false
35+
hasGoBuildIDSection := false
3636
for _, s := range f.Sections {
3737
if s.Name == ".note.go.buildid" {
38-
hasBuildIDSection = true
38+
hasGoBuildIDSection = true
3939
}
4040
}
4141

42-
if hasBuildIDSection {
42+
if hasGoBuildIDSection {
4343
f.Close()
4444

45-
id, err := gobuildid.ReadFile(path)
45+
id, err := fastGoBuildID(path)
46+
if err == nil && id != "" {
47+
return hex.EncodeToString([]byte(id)), nil
48+
}
49+
50+
id, err = gobuildid.ReadFile(path)
4651
if err != nil {
4752
return elfBuildID(path)
4853
}
@@ -54,6 +59,24 @@ func BuildID(path string) (string, error) {
5459
return elfBuildID(path)
5560
}
5661

62+
func fastGoBuildID(path string) (string, error) {
63+
elfFile, err := elf.Open(path)
64+
if err != nil {
65+
return "", fmt.Errorf("failed to open elf: %w", err)
66+
}
67+
defer elfFile.Close()
68+
69+
s := elfFile.Section(".note.go.buildid")
70+
if s == nil {
71+
return "", fmt.Errorf("failed to find .note.go.buildid section")
72+
}
73+
data, err := s.Data()
74+
if err != nil {
75+
return "", fmt.Errorf("failed to read .note.go.buildid section: %w", err)
76+
}
77+
return string(data), nil
78+
}
79+
5780
func elfBuildID(file string) (string, error) {
5881
f, err := os.Open(file)
5982
if err != nil {

0 commit comments

Comments
 (0)