Skip to content

Commit 2cfcb43

Browse files
authored
feat: compile in advance using CO-RE (#3)
* feat: compile in advance * feat: use CO:RE for task_struct * Remove exectrace.Filter struct * Add github actions workflows for lint/fmt * Fix typo * Improve CI coverage, check for genned files * Shellcheck * Rename FilterPidNS to PidNS * Make arglen and argsize private
1 parent a0833ae commit 2cfcb43

36 files changed

+1159
-698
lines changed

.clang-tidy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Checks: "clang-diagnostic-*,clang-analyzer-*,-*,cert-*,linuxkernel-*,clang-analyzer-*,llvm-*,performance-*,portability-*,readability-*"
2+
WarningsAsErrors: "*"

.editorconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
7+
[{*.go,*.c,*.h,Makefile}]
8+
indent_style = tab
9+
indent_size = 4
10+
11+
[{*.ya?ml,*.json,.prettierrc]
12+
indent_style = space
13+
indent_size = 2

.github/workflows/gen.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: gen
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- "*"
9+
10+
pull_request:
11+
branches:
12+
- main
13+
14+
workflow_dispatch:
15+
16+
permissions:
17+
actions: none
18+
checks: none
19+
contents: read
20+
deployments: none
21+
issues: none
22+
packages: none
23+
pull-requests: none
24+
repository-projects: none
25+
security-events: none
26+
statuses: none
27+
28+
jobs:
29+
handler-elf:
30+
name: handler-elf
31+
runs-on: ubuntu-20.04
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@v2
35+
36+
- name: Run make
37+
run: make
38+
39+
- name: Check for unstaged files
40+
run: ./ci/scripts/check_unstaged.sh

.github/workflows/quality.yml

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
name: quality
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- "*"
9+
10+
pull_request:
11+
branches:
12+
- main
13+
14+
workflow_dispatch:
15+
16+
permissions:
17+
actions: none
18+
checks: none
19+
contents: read
20+
deployments: none
21+
issues: none
22+
packages: none
23+
pull-requests: none
24+
repository-projects: none
25+
security-events: none
26+
statuses: none
27+
28+
jobs:
29+
fmt-go:
30+
name: fmt/go
31+
runs-on: ubuntu-20.04
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@v2
35+
36+
- name: Install Go
37+
uses: actions/setup-go@v2
38+
with:
39+
go-version: "^1.16.12"
40+
41+
- name: Run make fmt/go
42+
run: make fmt/go
43+
44+
- name: Check for unstaged files
45+
run: ./ci/scripts/check_unstaged.sh
46+
47+
fmt-prettier:
48+
name: fmt/prettier
49+
runs-on: ubuntu-20.04
50+
steps:
51+
- name: Checkout
52+
uses: actions/checkout@v2
53+
54+
- name: Install Node.js
55+
uses: actions/setup-node@v2
56+
with:
57+
node-version: "14"
58+
59+
- name: Install prettier
60+
run: npm install --global prettier
61+
62+
- name: Run make fmt/prettier
63+
run: make fmt/prettier
64+
65+
- name: Check for unstaged files
66+
run: ./ci/scripts/check_unstaged.sh
67+
68+
lint-go:
69+
name: lint/go
70+
runs-on: ubuntu-20.04
71+
steps:
72+
- name: Checkout
73+
uses: actions/checkout@v2
74+
75+
- name: Install Go
76+
uses: actions/setup-go@v2
77+
with:
78+
go-version: "^1.16.12"
79+
80+
- name: Install golangci-lint
81+
run: |
82+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
83+
| sh -s -- -b $(go env GOPATH)/bin v1.43.0
84+
85+
# Linting needs to be done on each build variation of GOOS.
86+
- name: Run make lint/go/linux
87+
run: make lint/go/linux
88+
89+
# The windows and darwin builds include the same files.
90+
- name: Run make lint/go/other
91+
run: make lint/go/other
92+
93+
lint-c:
94+
name: lint/c
95+
runs-on: ubuntu-20.04
96+
steps:
97+
- name: Checkout
98+
uses: actions/checkout@v2
99+
100+
- name: Run make lint/c
101+
run: make lint/c
102+
103+
lint-shellcheck:
104+
name: lint/shellcheck
105+
runs-on: ubuntu-20.04
106+
steps:
107+
- name: Checkout
108+
uses: actions/checkout@v2
109+
110+
- name: Install shellcheck
111+
run: sudo apt install -y shellcheck
112+
113+
- name: Run make lint/shellcheck
114+
run: make lint/shellcheck

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,12 @@
1616

1717
# Object files
1818
*.o
19+
!bpf/handler-*.o
1920
*.obj
2021
*.elf
22+
23+
# Editor folders
24+
.vscode
25+
26+
# Make outputs
27+
.clang-image

Makefile

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
SHELL := bash
2+
# Exit on failure
3+
.SHELLFLAGS := -ceu
4+
.DELETE_ON_ERROR:
5+
.ONESHELL:
6+
7+
# Specify the verbose flag to see all command output.
8+
ifndef VERBOSE
9+
.SILENT:
10+
endif
11+
12+
CXX = clang-13
13+
14+
.PHONY: all
15+
all: bpf/handler-bpfeb.o bpf/handler-bpfel.o
16+
17+
.PHONY: clean
18+
clean:
19+
rm -rf bpf/handler-bpfeb.o bpf/handler-bpfel.o
20+
21+
ci/.clang-image: ci/images/clang-13/Dockerfile
22+
./ci/scripts/clang_image.sh
23+
touch ci/.clang-image
24+
25+
# bpfeb is big endian, bpfel is little endian.
26+
bpf/handler-bpfeb.o bpf/handler-bpfel.o: bpf/*.h bpf/*.c ci/.clang-image
27+
./ci/scripts/build_handler.sh "$(@F)"
28+
29+
.PHONY: fmt
30+
fmt: fmt/go fmt/prettier
31+
32+
.PHONY: fmt/go
33+
fmt/go:
34+
go fmt ./...
35+
36+
.PHONY: fmt/prettier
37+
fmt/prettier:
38+
# Config file: .prettierrc
39+
prettier -w .
40+
41+
.PHONY: lint
42+
lint: lint/go lint/c lint/shellcheck
43+
44+
.PHONY: lint/go
45+
lint/go: lint/go/linux lint/go/other
46+
47+
.PHONY: lint/go/linux
48+
lint/go/linux:
49+
# Config file: .golangci.yml
50+
golangci-lint run ./...
51+
52+
.PHONY: lint/go/other
53+
lint/go/other:
54+
# The windows and darwin builds include the same files.
55+
# Config file: .golangci.yml
56+
GOOS=windows golangci-lint run ./...
57+
58+
.PHONY: lint/c
59+
lint/c: ci/.clang-image
60+
# Config file: .clang-tidy
61+
./ci/scripts/clang.sh clang-tidy-13 --config-file ../.clang-tidy ./handler.c
62+
63+
.PHONY: lint/shellcheck
64+
lint/shellcheck:
65+
./ci/scripts/shellcheck.sh

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ $ sudo exectrace --compiler clang-13
4343
You can look at the example program [exectrace](./cmd/exectrace/main.go) for a
4444
comprehensive program using this library.
4545

46+
## Development
47+
48+
Since the eBPF program is packaged as a Go library, the program needs to be
49+
compiled and included in the repo. If you make changes to files under the `bpf`
50+
directory, you should run `make` and include the `.o` files in that directory in
51+
your commit if they changed. CI will ensure that this is done correctly.
52+
53+
You will probably need the following tools:
54+
55+
- Docker (clang is run within a Docker container for reproducibility)
56+
- `golangci-lint`
57+
- `prettier`
58+
- `shellcheck`
59+
4660
## Status: In Development
4761

4862
The library is currently under heavy development as we develop it out to suit

bpf.go

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,97 @@
1+
//go:build linux
2+
// +build linux
3+
14
package exectrace
25

3-
import "bytes"
6+
import (
7+
"bytes"
8+
"runtime"
9+
"sync"
10+
11+
"github.com/cilium/ebpf"
12+
"github.com/cilium/ebpf/rlimit"
13+
"github.com/hashicorp/go-multierror"
14+
"golang.org/x/xerrors"
15+
)
16+
17+
var (
18+
errObjectsClosed = xerrors.New("objects are closed")
19+
removeMemlockOnce sync.Once
20+
21+
// collectionOpts used for loading the BPF objects.
22+
collectionOpts = &ebpf.CollectionOptions{
23+
Programs: ebpf.ProgramOptions{
24+
// While debugging, it may be helpful to set this value to be much
25+
// higher (i.e. * 1000).
26+
LogSize: ebpf.DefaultVerifierLogSize,
27+
},
28+
}
29+
)
30+
31+
// loadBPFObjects reads and parses the programs and maps out of the embedded
32+
// BPF program.
33+
func loadBPFObjects() (*bpfObjects, error) {
34+
// Allow the current process to lock memory for eBPF resources. This does
35+
// nothing on 5.11+ kernels which don't need this.
36+
var err error
37+
removeMemlockOnce.Do(func() {
38+
err = rlimit.RemoveMemlock()
39+
})
40+
if err != nil {
41+
return nil, xerrors.Errorf("remove kernel memlock: %w", err)
42+
}
43+
44+
r := bytes.NewReader(bpfProgram)
45+
spec, err := ebpf.LoadCollectionSpecFromReader(r)
46+
if err != nil {
47+
return nil, xerrors.Errorf("load collection from reader: %w", err)
48+
}
49+
50+
objs := &bpfObjects{
51+
closeLock: sync.Mutex{},
52+
closed: make(chan struct{}),
53+
}
54+
err = spec.LoadAndAssign(objs, collectionOpts)
55+
if err != nil {
56+
return nil, xerrors.Errorf("load and assign specs: %w", err)
57+
}
58+
59+
return objs, nil
60+
}
61+
62+
type bpfObjects struct {
63+
EnterExecveProg *ebpf.Program `ebpf:"enter_execve"`
64+
EventsMap *ebpf.Map `ebpf:"events"`
65+
FiltersMap *ebpf.Map `ebpf:"filters"`
66+
67+
closeLock sync.Mutex
68+
closed chan struct{}
69+
}
70+
71+
func (o *bpfObjects) Close() error {
72+
o.closeLock.Lock()
73+
defer o.closeLock.Unlock()
74+
select {
75+
case <-o.closed:
76+
return errObjectsClosed
77+
default:
78+
}
79+
close(o.closed)
80+
runtime.SetFinalizer(o, nil)
81+
82+
var merr error
83+
if o.EnterExecveProg != nil {
84+
err := o.EnterExecveProg.Close()
85+
if err != nil {
86+
merr = multierror.Append(merr, xerrors.Errorf(`close BPF program "enter_execve": %w`, err))
87+
}
88+
}
89+
if o.EventsMap != nil {
90+
err := o.EventsMap.Close()
91+
if err != nil {
92+
merr = multierror.Append(merr, xerrors.Errorf(`close BPF map "events": %w`, err))
93+
}
94+
}
495

5-
// LoadBPFObjectsBytes is a helper for LoadBPFObjects that automatically wraps
6-
// the given byte slice with a bytes.Reader.
7-
func LoadBPFObjectsBytes(p []byte) (BPFObjects, error) {
8-
return LoadBPFObjects(bytes.NewReader(p))
96+
return merr
997
}

0 commit comments

Comments
 (0)