Value *pid = b_.CreateLShr(b_.CreateGetPidTgid(), 32);
Value *addr_offset = b_.CreateGEP(buf, b_.getInt64(0));
Value *pid_offset = b_.CreateGEP(buf, {b_.getInt64(0), b_.getInt64(8)});
call.vargs->front()->accept(*this);
b_.CreateStore(expr_, addr_offset);
b_.CreateStore(pid, pid_offset);
expr_ = buf;
```
These are llvm [Intermediate Representation](https://en.wikipedia.org/wiki/Intermediate_representation)\(IR\) functions that emit an llvm assembly-like language which can be compiled directly to BPF, thanks to llvm's BPF support. If you use bpftrace -d, you'll see this llvm assembly:
```shell
bpftrace -d-e'kprobe:do_nanosleep { printf("%s is sleeping\n", comm); }'
If there's one gotcha I would like to mention, it's the use of CreateGEP() (Get Element Pointer). It's needed when dereferencing at an offset in a buffer, and it's tricky to use.
Let's go through some codegen examples.
## 1. Codegen: Sum
We can explore and get the hang of llvm assembly by writing some simple C programs and compiling them using clang. Since llvm assembly maps to llvm IR, I've sometimes prototyped my codegen_llvm.cpp IR this way: writing a C program to produce the llvm assembly, and then manually mapping it back to llvm IR.
That's just my mental conversion. I haven't tested this and it may have a bug. But this should be enough to illustrate the idea.
## 2. Codegen: curtask
If you need to add support to a BPF kernel function that bpftrace does not yet call, this is a simple example. It adds a `curtask` builtin that calls BPF_FUNC_get_current_task. See [bcc Kernel Versions](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md) for documentation on these BPF functions. The commit is:
You'll also notice that the builtins finish by setting `expr_` to the final result. This is taking the node in the AST and replacing it with the computed expression. Calls don't necessarily do this: for example, `reg()` sets `expr_` since it returns a value, but `printf()` sets `expr_` to `nullptr`, since it does not return a value.
These are examples of adding new map functions, and the required components. Since the functions themselves are simple, they are good examples of codegen. They were all added in a single commit:
This also shows the bpftrace components that were added to support these: `BPFtrace::print_map_stats()`, `BPFtrace::max_value()`, `BPFtrace::min_value()`.
# Probes
Probes are reasonably straightforward. We use libbpf/libbcc, both from [bcc](https://github.com/iovisor/bcc), to create the probes via functions such as `bpf_attach_kprobe()`, `bpf_attach_uprobe()`, and `bpf_attach_tracepoint()`. We also use USDT helpers such as `bcc_usdt_enable_probe()`
## 1. Probes: Interval
The addition of the `interval` probe type is a simple example of adding a probe, and the components required: