Skip to content

Commit

Permalink
docs: add documentation
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Beckers <[email protected]>
  • Loading branch information
ti-mo committed Sep 28, 2023
1 parent c589daf commit 8c84560
Show file tree
Hide file tree
Showing 43 changed files with 3,486 additions and 0 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ env:
CI_MIN_CLANG_VERSION: '11'
go_version: '~1.21'
prev_go_version: '~1.20'
# This needs to match whatever Netlify supports.
# Also defined in Pipfile.
python_version: '~3.8'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -70,6 +73,39 @@ jobs:
go build -v ./...
go test -c -o /dev/null ./... >/dev/null
build-docs:
name: Build Documentation
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
with:
# The mkdocs git-authors plugin needs access to the full revision
# history to correctly generate its statistics.
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '${{ env.go_version }}'

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '${{ env.python_version }}'
cache: 'pipenv'

- name: Install pipenv
run: pip3 install pipenv

- name: Install Dependencies
run: pipenv install
working-directory: ./docs

- name: Build Documentation
run: make build
working-directory: ./docs

test-on-prev-go:
name: Run tests on previous stable Go
runs-on: ubuntu-latest-4cores-16gb
Expand Down
6 changes: 6 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Python
__pycache__

# Build output
build/
site/
22 changes: 22 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
build: pipenv
@# Run a production build of the documentation. Strict mode makes warnings fatal.
pipenv run mkdocs build --strict

@# Build main packages, discarding build output.
go build -v ./...

@# build _test.go files containing Doc* functions and skip all tests.
go test -c -o /dev/null ./... >/dev/null

preview: pipenv
pipenv run mkdocs serve

shell: pipenv
pipenv shell

pipenv:
ifeq (, $(shell command -v pipenv 2> /dev/null))
$(error "pipenv is not installed, exiting..")
endif

.PHONY: pipenv
18 changes: 18 additions & 0 deletions docs/Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
mkdocs = "*"
pymdown-extensions = "*"
mkdocs-material = "9.3.2"
mkdocs-macros-plugin = "*"
mkdocs-git-revision-date-localized-plugin = "*"
mkdocs-git-authors-plugin = "*"

[dev-packages]

# Whatever Netlify's Ubuntu version uses.
[requires]
python_version = "3.8"
617 changes: 617 additions & 0 deletions docs/Pipfile.lock

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# epbf-go documentation

The documentation project uses Pipenv to manage its dependencies, which will
automatically create a Python virtualenv when invoked from this subdirectory.
Follow your distribution's documentation for installing `pipenv`. You may also
need `pyenv` to install a different Python version if your distribution doesn't
provide the version specified in the `Pipfile`.

Host a live preview of the documentation at http://127.0.0.1:8000:

`make preview`

Build the documentation, output to the site/ directory. This is a self-contained
production copy that can be uploaded to hosting.

`make build`

To enter the virtualenv with all the documentation's Python dependencies
installed:

`make shell`
11 changes: 11 additions & 0 deletions docs/ebpf/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# About {{ proj }}

The project was initially created in 2017 as
[`newtools/ebpf`](https://github.com/newtools/ebpf). It quickly gained traction
within the Go community, especially for projects that couldn't or wouldn't build
upon the CGo-based BCC bindings at the time (`gobpf`).

Since then, it's been widely adopted by hundreds of open-source projects, large
industry players, as well as startups building their businesses on top of eBPF.
It continues to evolve thanks to its thriving community and close collaboration
with the upstream Linux project.
5 changes: 5 additions & 0 deletions docs/ebpf/bpf2go/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
!!! incomplete
Give an overview of bpf2go's features and flags. Note that [Getting
Started](../guides/getting-started.md) already acts as an introduction.

Might not warrant its own section, could move to 'Concepts'.
6 changes: 6 additions & 0 deletions docs/ebpf/btf/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
!!! incomplete
Document package `btf/` here. Give a small overview of its API, how to
interact with user BTF and kernel BTF and what the difference is between
the two and what their purpose is within the bigger picture.

Might not warrant its own section, could move to 'Concepts'.
4 changes: 4 additions & 0 deletions docs/ebpf/concepts/features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
!!! incomplete
On this page, we want to document package `features/`, a bit of its
background, and how it can be used. Comparison to `bpftool feature probe`
would be useful.
108 changes: 108 additions & 0 deletions docs/ebpf/concepts/loader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Loading Objects

{{ proj }} ships an eBPF object (ELF) loader that aims to be compatible with the
upstream libbpf and iproute2 (`tc`/`ip`) projects. An ELF is typically obtained
by compiling a eBPF C program using the LLVM toolchain (`clang`).

This page describes the journey from compiled eBPF ELF to resources in the
kernel. This involves parsing the ELF into intermediate Go (Spec) types that
can be modified and copied before loading them into the kernel.

```mermaid
graph LR
ELF --> ProgramSpec --> Program
ELF --> Types
ELF --> MapSpec --> Map
Map & Program --> Links
subgraph Collection
Program & Map
end
subgraph CollectionSpec
ProgramSpec & MapSpec & Types
end
```

## {{ godoc('CollectionSpec') }}

A CollectionSpec represents eBPF objects extracted from an ELF, and can be
obtained by calling {{ godoc('LoadCollectionSpec') }}. In the examples below, we
declare a Map and Program in eBPF C, then load and inspect them using Go. Use
the tabs to explore the Go and C counterparts below.

=== ":simple-go: Go"
{{ go_example('DocLoadCollectionSpec', title='Parse ELF and inspect its CollectionSpec') | indent(4) }}

!!! warning ""
All of a Spec's attributes can be modified, and those modifications
influence the resources created in the kernel. Be aware that doing so
may invalidate any assumptions made by the compiler, resulting in maps
or programs being rejected by the kernel. Proceed with caution.

=== ":ebee-color: eBPF C"
{{ c_example('DocMyMapProgram', title='Declare a minimal map and a program') | indent(4) }}

!!! tip ""
See [Section Naming](section-naming.md) to learn about the use of the
`SEC()` macro in the example above.

## {{ godoc('NewCollection') }}

After parsing the ELF into a CollectionSpec, it can be loaded into the kernel
using {{ godoc('NewCollection') }}, resulting in a {{ godoc('Collection') }}.

{{ go_example('DocNewCollection') }}

!!! note ""
{{ godoc('Collection.Close') }} closes all Maps and Programs in the
Collection. Interacting with any resources after `Close()` will return an
error, since their underlying file descriptors will be closed. See [Object
Lifecycle](object-lifecycle.md) to gain a better understanding of how {{
proj }} manages its resources and for best practices handling Maps and
Programs.

## {{ godoc('CollectionSpec.LoadAndAssign', short=True) }}

LoadAndAssign is a convenience API that can be used instead of `NewCollection`.
It has two major benefits:

- It automates pulling Maps and Programs out of a Collection. No more `#!go if m
:= coll.Maps["my_map"]; m == nil { return ... }`.
- **Selective loading of Maps and Programs!** Only resources of interest and
their dependencies are loaded into the kernel. Great for working with large
CollectionSpecs that only need to be partially loaded.

First, declare a struct that will receive pointers to a Map and a Program after
loading them into the kernel. Give it a `#!go Close()` method to make cleanup
easier.

{{ go_example('DocLoadAndAssignObjs', title='Declare a custom struct myObjs') }}

!!! note ""
Use [bpf2go](../bpf2go/index.md) if the preceding code snippet looks
tedious. bpf2go can generate this kind of boilerplate code automatically
and will make sure it stays in sync with your C code.

Next, instantiate a variable of our newly-declared type and pass its pointer to
`LoadAndAssign`.

{{ go_example('DocLoadAndAssign', title='Pass a custom struct to LoadAndAssign') }}

!!! warning ""
If your use case requires dynamically renaming keys in CollectionSpec.Maps,
you may need to use NewCollection instead. Map and Program names in struct
tags are baked into the Go binary at compile time.

## Type Information (BTF)

If an eBPF ELF was built with `clang -g`, it will automatically contain BTF type
information. This information can be accessed programmatically through {{
godoc('CollectionSpec.Types') }}. Note that this field will be `nil` if the ELF
was built without BTF.

{{ go_example('DocBTFTypeByName') }}

!!! note ""
Many eBPF features rely on ELFs to be built with BTF, and there is
little to be gained by opting out of it. `clang -g` also includes DWARF
information in the ELF which can be safely removed with `llvm-strip`. eBPF
does not rely on DWARF information.
92 changes: 92 additions & 0 deletions docs/ebpf/concepts/object-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
!!! info ""
This is an advanced topic and does not need to be fully understood in order
to get started writing useful tools.

If you find yourself debugging unexpectedly-detached programs, resource
leaks, or you want to gain a deeper understanding of how eBPF objects are
managed by {{ proj }}, this page should prove helpful.

## File Descriptors and Go

Interacting with eBPF objects from user space is done using file descriptors.
Counter-intuitively, 'file' descriptors are used as references to many types of
kernel resources in modern Linux, not just files. In {{ proj }}, {{ godoc('Map')
}}, {{ godoc('Program') }} and {{ godoc('link/Link') }} are all modeled around
these underlying file descriptors.

Go, being a garbage-collected language, automatically manages the lifecycle of
Go objects. Keeping in line with the standard library's `os.File` and friends,
eBPF resources in {{ proj }} were designed in a way so their underlying file
descriptors are closed when their Go objects are garbage collected. This
generally prevents runaway resource leaks, but is not without its drawbacks.

This has subtle but important repercussions for BPF, since this means the Go
runtime will call `Close()` on an object's underlying file descriptor if the
object is no longer reachable by the garbage collector. For example, this can
happen if an object is created in a function, but is not returned to the caller.
One type of map, {{ godoc('ProgramArray') }}, is particularly sensitive to this.
More about that in [Program Arrays](#program-arrays).

## Extending Object Lifetime

### Pinning

Aside from file descriptors, BPF provides another method of creating references
to eBPF objects: pinning. This is the concept of associating a file on a virtual
file system (the BPF File System, bpffs for short) with a BPF resource like a
Map, Program or Link. Pins can be organized into arbitrary directory structures,
just like on any other file system.

When the Go process exits, the pin will maintain a reference to the object,
preventing it from being automatically destroyed. In this scenario, removing the
pin using plain `rm` will remove the last reference, causing the kernel to
destroy the object. If you're holding an active object in Go, you can also call
{{ godoc('Map.Unpin') }}, {{ godoc('Program.Unpin') }} or {{
godoc('link/Link.Unpin') }} if the object was previously pinned.

!!! warning
Pins do **not** persist through a reboot!

A common use case for pinning is sharing eBPF objects between processes. For
example, one could create a Map from Go, pin it, and inspect it using `bpftool
map dump pinned /sys/fs/bpf/my_map`.

### Attaching

Attaching a Program to a hook acts as a reference to a Program, since the kernel
needs to be able to execute the program's instructions at any point. More on
that in [the section on attaching programs](../programs/attaching.md).

For legacy reasons, some {{ godoc('link/Link') }} types don't support pinning.
It is generally safe to assume these links will persist beyond the lifetime of
the Go application.

## :warning: Program Arrays

A {{ godoc('ProgramArray') }} is a Map type that holds references to other
Programs. This allows programs to 'tail call' into other programs, useful for
splitting up long and complex programs.

Program Arrays have a unique property: they allow cyclic dependencies to be
created between the Program Array and a Program (e.g. allowing programs to call
into themselves).To avoid ending up with a set of programs loaded into the
kernel that cannot be freed, the kernel maintains a hard rule: **Program Arrays
require at least one open file descriptor or bpffs pin**.

!!! warning
If all user space/bpffs references are gone, **any tail calls into the array
will fail**, but the Map itself will remain loaded as long as there are
programs that use it. This property, combined with interactions with Go's
garbage collector previously described in [File Descriptors and
Go](#file-descriptors-and-go), is a great source of bugs.

A few tips to handle this problem correctly:

- Use {{ godoc('CollectionSpec.LoadAndAssign') }}. It will refuse to load the
CollectionSpec if doing so would result in a Program Array without a userspace
reference.
- Pin Program Arrays if execution of your eBPF code needs to continue past the
lifetime of your Go application, e.g. for upgrades or short-lived CLI tools.
- Retain references to the Map at all times in long-running applications. Note
that `#!go defer m.Close()` makes Go retain a reference until the end of the
current scope.
Loading

0 comments on commit 8c84560

Please sign in to comment.