Skip to main content

bpftrace Standard Library (0.24)

This includes builtins, functions, macros, and map value functions. The boundaries for the first three are blurred, by design, to allow for more flexible usage and are grouped below as "Helpers". For example pid and pid() are equivalent; both yielding the process id. Basically all functions or macros that don't have arguments or have default arguments can be invoked with or without the call syntax.

async helpers are asynchronous, which can lead to unexpected behaviour. See the Invocation Mode section for more information.

compile time helpers are evaluated at compile time, a static value will be compiled into the program.

unsafe helpers can have dangerous side effects and should be used with care, the --unsafe flag is required for use.

Helpers

assert

  • void assert(bool condition, string message)

Simple assertion macro that will exit the entire script with an error code if the condition is not met.

bswap

  • uint8 bswap(uint8 n)
  • uint16 bswap(uint16 n)
  • uint32 bswap(uint32 n)
  • uint64 bswap(uint64 n)

bswap reverses the order of the bytes in integer n. In case of 8 bit integers, n is returned without being modified. The return type is an unsigned integer of the same width as n.

buf

  • buffer buf(void * data, [int64 length])

buf reads length amount of bytes from address data. The maximum value of length is limited to the BPFTRACE_MAX_STRLEN variable. For arrays the length is optional, it is automatically inferred from the signature.

buf is address space aware and will call the correct helper based on the address space associated with data.

The buffer object returned by buf can safely be printed as a hex encoded string with the %r format specifier.

Bytes with values >=32 and <=126 are printed using their ASCII character, other bytes are printed in hex form (e.g. \x00). The %rx format specifier can be used to print everything in hex form, including ASCII characters. The similar %rh format specifier prints everything in hex form without \x and with spaces between bytes (e.g. 0a fe).

interval:s:1 {
printf("%r\n", buf(kaddr("avenrun"), 8));
}
\x00\x03\x00\x00\x00\x00\x00\x00
\xc2\x02\x00\x00\x00\x00\x00\x00

cat

  • void cat(string namefmt, [...args])

async

Dump the contents of the named file to stdout. cat supports the same format string and arguments that printf does. If the file cannot be opened or read an error is printed to stderr.

tracepoint:syscalls:sys_enter_execve {
cat("/proc/%d/maps", pid);
}
55f683ebd000-55f683ec1000 r--p 00000000 08:01 1843399                    /usr/bin/ls
55f683ec1000-55f683ed6000 r-xp 00004000 08:01 1843399 /usr/bin/ls
55f683ed6000-55f683edf000 r--p 00019000 08:01 1843399 /usr/bin/ls
55f683edf000-55f683ee2000 rw-p 00021000 08:01 1843399 /usr/bin/ls
55f683ee2000-55f683ee3000 rw-p 00000000 00:00 0

cgroup

  • uint64 cgroup()
  • uint64 cgroup

ID of the cgroup the current process belongs to

Only works with cgroupv2

This utilizes the BPF helper get_current_cgroup_id

cgroup_path

  • cgroup_path_t cgroup_path(int cgroupid, string filter)

Convert cgroup id to cgroup path. This is done asynchronously in userspace when the cgroup_path value is printed, therefore it can resolve to a different value if the cgroup id gets reassigned. This also means that the returned value can only be used for printing.

A string literal may be passed as an optional second argument to filter cgroup hierarchies in which the cgroup id is looked up by a wildcard expression (cgroup2 is always represented by "unified", regardless of where it is mounted).

The currently mounted hierarchy at /sys/fs/cgroup is used to do the lookup. If the cgroup with the given id isn’t present here (e.g. when running in a Docker container), the cgroup path won’t be found (unlike when looking up the cgroup path of a process via /proc/.../cgroup).

BEGIN {
$cgroup_path = cgroup_path(3436);
print($cgroup_path);
print($cgroup_path); /* This may print a different path */
printf("%s %s", $cgroup_path, $cgroup_path); /* This may print two different paths */
}

cgroupid

  • uint64 cgroupid(const string path)

compile time

cgroupid retrieves the cgroupv2 ID of the cgroup available at path.

BEGIN {
print(cgroupid("/sys/fs/cgroup/system.slice"));
}

clear

  • void clear(map m)

async

Clear all keys/values from map m.

interval:ms:100 {
@[rand % 10] = count();
}

interval:s:10 {
print(@);
clear(@);
}

comm

  • string comm()
  • string comm

Name of the current thread

This utilizes the BPF helper get_current_comm

cpid

  • uint32 cpid()
  • uint32 cpid

Child process ID, if bpftrace is invoked with -c

cpu

  • uint32 cpu()
  • uint32 cpu

ID of the processor executing the BPF program

BPF program, in this case, is the probe body

This utilizes the BPF helper raw_smp_processor_id

curtask

  • uint64 curtask()
  • uint64 curtask

Pointer to struct task_struct of the current task

This utilizes the BPF helper get_current_task

delete

  • bool delete(map m, mapkey k)
  • deprecated bool delete(mapkey k)

Delete a single key from a map. For scalar maps (e.g. no explicit keys), the key is omitted and is equivalent to calling clear. For map keys that are composed of multiple values (e.g. @mymap[3, "hello"] = 1 - remember these values are represented as a tuple) the syntax would be: delete(@mymap, (3, "hello"));

If deletion fails (e.g. the key doesn’t exist) the function returns false (0). Additionally, if the return value for delete is discarded, and deletion fails, you will get a warning.

@a[1] = 1;

delete(@a, 1); // no warning (the key exists)

if (delete(@a, 2)) { // no warning (return value is used)
...
}

$did_delete = delete(@a, 2); // no warning (return value is used)

delete(@a, 2); // warning (return value is discarded and the key doesn’t exist)

The, now deprecated, API (supported in version <= 0.21.x) of passing map arguments with the key is still supported: e.g. delete(@mymap[3, "hello"]);.

kprobe:dummy {
@scalar = 1;
delete(@scalar); // ok
@single["hello"] = 1;
delete(@single, "hello"); // ok
@associative[1,2] = 1;
delete(@associative, (1,2)); // ok
delete(@associative); // error
delete(@associative, 1); // error

// deprecated but ok
delete(@single["hello"]);
delete(@associative[1, 2]);
}

elapsed

  • uint64 elapsed()
  • uint64 elapsed

ktime_get_ns - ktime_get_boot_ns

errorf

  • void errorf(const string fmt, args...)

async

errorf() formats and prints data (similar to printf) as an error message with the source location.

BEGIN { errorf("Something bad with args: %d, %s", 10, "arg2"); }

Prints:

EXPECT stdin:1:9-62: ERROR: Something bad with args: 10, arg2

exit

  • void exit([int code])

async

Terminate bpftrace, as if a SIGTERM was received. The END probe will still trigger (if specified) and maps will be printed. An optional exit code can be provided.

BEGIN {
exit();
}

Or

BEGIN {
exit(1);
}

func

  • string func()
  • string func

Name of the current function being traced (kprobes,uprobes,fentry)

getopt

  • bool getopt(string arg_name)
  • string getopt(string arg_name, string default_value)
  • int getopt(string arg_name, int default_value)
  • bool getopt(string arg_name, bool default_value)

Get the named command line argument/option e.g.

# bpftrace -e 'BEGIN { print(getopt("hello", 1)); }' -- --hello=5

getopt defines the type of the argument by the default value’s type. If no default type is provided, the option is treated like a boolean arg e.g. getopt("hello") would evaluate to false if --hello is not specified on the command line or true if --hello is passed or set to one of the following values: true, 1. Additionally, boolean args accept the following false values: 0, false e.g. --hello=false. If the arg is not set on the command line, the default value is used.

# bpftrace -e 'BEGIN { print((getopt("aa", 10), getopt("bb", "hello"), getopt("cc"), getopt("dd", false))); }' -- --cc --bb=bye

gid

  • uint64 gid()
  • uint64 gid

Group ID of the current thread, as seen from the init namespace

This utilizes the BPF helper get_current_uid_gid

has_key

  • boolean has_key(map m, mapkey k)

Return true (1) if the key exists in this map. Otherwise return false (0). Error if called with a map that has no keys (aka scalar map). Return value can also be used for scratch variables and map keys/values.

kprobe:dummy {
@associative[1,2] = 1;
if (!has_key(@associative, (1,3))) { // ok
print(("bye"));
}

@scalar = 1;
if (has_key(@scalar)) { // error
print(("hello"));
}

$a = has_key(@associative, (1,2)); // ok
@b[has_key(@associative, (1,2))] = has_key(@associative, (1,2)); // ok
}

jiffies

  • uint64 jiffies()
  • uint64 jiffies

Jiffies of the kernel

On 32-bit systems, using this builtin might be slower

This utilizes the BPF helper get_jiffies_64

join

  • void join(char *arr[], [char * sep = ' '])

async

join joins a char * arr with sep as separator into one string. This string will be printed to stdout directly, it cannot be used as string value.

The concatenation of the array members is done in BPF and the printing happens in userspace.

tracepoint:syscalls:sys_enter_execve {
join(args.argv);
}

kaddr

  • uint64 kaddr(const string name)

compile time

Get the address of the kernel symbol name.

interval:s:1 {
$avenrun = kaddr("avenrun");
$load1 = *$avenrun;
}

You can find all kernel symbols at /proc/kallsyms.

kptr

  • T * kptr(T * ptr)

Marks ptr as a kernel address space pointer. See the address-spaces section for more information on address-spaces. The pointer type is left unchanged.

kstack

  • kstack_t kstack([StackMode mode, ][int limit])

These are implemented using BPF stack maps.

kprobe:ip_output { @[kstack()] = count(); }

/*
* Sample output:
* @[
* ip_output+1
* tcp_transmit_skb+1308
* tcp_write_xmit+482
* tcp_release_cb+225
* release_sock+64
* tcp_sendmsg+49
* sock_sendmsg+48
* sock_write_iter+135
* __vfs_write+247
* vfs_write+179
* sys_write+82
* entry_SYSCALL_64_fastpath+30
* ]: 1708
*/

Sampling only three frames from the stack (limit = 3):

kprobe:ip_output { @[kstack(3)] = count(); }

/*
* Sample output:
* @[
* ip_output+1
* tcp_transmit_skb+1308
* tcp_write_xmit+482
* ]: 1708
*/

You can also choose a different output format. Available formats are bpftrace, perf, and raw (no symbolication):

kprobe:ip_output { @[kstack(perf, 3)] = count(); }

/*
* Sample output:
* @[
* ffffffffb4019501 do_mmap+1
* ffffffffb401700a sys_mmap_pgoff+266
* ffffffffb3e334eb sys_mmap+27
* ]: 1708
*/

ksym

  • ksym_t ksym(uint64 addr)

async

Retrieve the name of the function that contains address addr. The address to name mapping happens in user-space.

The ksym_t type can be printed with the %s format specifier.

kprobe:do_nanosleep
{
printf("%s\n", ksym(reg("ip")));
}

/*
* Sample output:
* do_nanosleep
*/

len

  • int64 len(map m)
  • int64 len(ustack stack)
  • int64 len(kstack stack)

For maps, return the number of elements in the map.

For kstack/ustack, return the depth (measured in # of frames) of the call stack.

macaddr

  • macaddr_t macaddr(char [6] mac)

Create a buffer that holds a macaddress as read from mac This buffer can be printed in the canonical string format using the %s format specifier.

kprobe:arp_create {
$stack_arg0 = *(uint8*)(reg("sp") + 8);
$stack_arg1 = *(uint8*)(reg("sp") + 16);
printf("SRC %s, DST %s\n", macaddr($stack_arg0), macaddr($stack_arg1));
}

/*
* Sample output:
* SRC 18:C0:4D:08:2E:BB, DST 74:83:C2:7F:8C:FF
*/

ncpus

  • uint64 ncpus()
  • uint64 ncpus

Number of CPUs

nsecs

  • timestamp nsecs([TimestampMode mode])
  • nsecs(monotonic) - nanosecond timestamp since boot, exclusive of time the system spent suspended (CLOCK_MONOTONIC)
  • nsecs(boot) - nanoseconds since boot, inclusive of time the system spent suspended (CLOCK_BOOTTIME)
  • nsecs(tai) - TAI timestamp in nanoseconds (CLOCK_TAI)
  • nsecs(sw_tai) - approximation of TAI timestamp in nanoseconds, is obtained through the "triple vdso sandwich" method. For older kernels without direct TAI timestamp access in BPF.

Returns a timestamp in nanoseconds, as given by the requested kernel clock. Defaults to boot if no clock is explicitly requested.

interval:s:1 {
$sw_tai1 = nsecs(sw_tai);
$tai = nsecs(tai);
$sw_tai2 = nsecs(sw_tai);
printf("sw_tai precision: %lldns\n", ($sw_tai1 + $sw_tai2)/2 - $tai);
}

/*
* Sample output:
* sw_tai precision: -98ns
* sw_tai precision: -99ns
* ...
*/

ntop

  • inet ntop([int64 af, ] int addr)
  • inet ntop([int64 af, ] char addr[4])
  • inet ntop([int64 af, ] char addr[16])

ntop returns the string representation of an IPv4 or IPv6 address. ntop will infer the address type (IPv4 or IPv6) based on the addr type and size. If an integer or char[4] is given, ntop assumes IPv4, if a char[16] is given, ntop assumes IPv6. You can also pass the address type (e.g. AF_INET) explicitly as the first parameter.

numaid

  • uint32 numaid()
  • uint32 numaid

ID of the NUMA node executing the BPF program

BPF program, in this case, is the probe body

This utilizes the BPF helper numa_node_id

offsetof

  • uint64 offsetof(STRUCT, FIELD[.SUBFIELD])
  • uint64 offsetof(EXPRESSION, FIELD[.SUBFIELD])

compile time

Returns offset of the field offset bytes in struct. Similar to kernel offsetof operator.

Support any number of sub field levels, for example:

struct Foo {
struct {
struct {
struct {
int d;
} c;
} b;
} a;
}
BEGIN {
@x = offsetof(struct Foo, a.b.c.d);
exit();
}

override

  • void override(uint64 rc)

unsafe

Kernel 4.16

This utilizes the BPF helper bpf_override

Supported probes

  • kprobe

When using override the probed function will not be executed and instead rc will be returned.

kprobe:__x64_sys_getuid
/comm == "id"/ {
override(2<<21);
}
uid=4194304 gid=0(root) euid=0(root) groups=0(root)

This feature only works on kernels compiled with CONFIG_BPF_KPROBE_OVERRIDE and only works on functions tagged ALLOW_ERROR_INJECTION.

bpftrace does not test whether error injection is allowed for the probed function, instead if will fail to load the program into the kernel:

ioctl(PERF_EVENT_IOC_SET_BPF): Invalid argument
Error attaching probe: 'kprobe:vfs_read'

path

  • char * path(struct path * path [, int32 size])

Kernel 5.10

This utilizes the BPF helper bpf_d_path

Return full path referenced by struct path pointer in argument. If size is set, the path will be clamped by size otherwise BPFTRACE_MAX_STRLEN is used.

If size is smaller than the resolved path, the resulting string will be truncated at the front rather than at the end.

This function can only be used by functions that are allowed to, these functions are contained in the btf_allowlist_d_path set in the kernel.

percpu_kaddr

  • uint64 *percpu_kaddr(const string name)
  • uint64 *percpu_kaddr(const string name, int cpu)

sync

Get the address of the percpu kernel symbol name for CPU cpu. When cpu is omitted, the current CPU is used.

interval:s:1 {
$proc_cnt = percpu_kaddr("process_counts");
printf("% processes are running on CPU %d\n", *$proc_cnt, cpu);
}

The second variant may return NULL if cpu is higher than the number of available CPUs. Therefore, it is necessary to perform a NULL-check on the result when accessing fields of the pointed structure, otherwise the BPF program will be rejected.

interval:s:1 {
$runqueues = (struct rq *)percpu_kaddr("runqueues", 0);
if ($runqueues != 0) { // The check is mandatory here
print($runqueues->nr_running);
}
}

pid

  • uint32 pid([curr_ns|init])
  • uint32 pid

Returns the process ID of the current thread. Defaults to curr_ns.

  • pid(curr_ns) - The process ID as seen from the PID namespace of bpftrace.
  • pid(init) - The process ID as seen from the initial PID namespace.

ppid

  • uint32 ppid(struct task_struct * task)

Get the pid of the parent process

print

  • void print(T val)

async

printf

  • void printf(const string fmt, args...)

async

printf() formats and prints data. It behaves similar to printf() found in C and many other languages.

The format string has to be a constant, it cannot be modified at runtime. The formatting of the string happens in user space. Values are copied and passed by value.

bpftrace supports all the typical format specifiers like %llx and %hhu. The non-standard ones can be found in the table below:

SpecifierTypeDescription
rbufferHex-formatted string to print arbitrary binary content returned by the buf function.
rhbufferPrints in hex-formatted string without \x and with spaces between bytes (e.g. 0a fe)

printf() can also symbolize enums as strings. User defined enums as well as enums defined in the kernel are supported. For example:

enum custom {
CUSTOM_ENUM = 3,
};

BEGIN {
$r = SKB_DROP_REASON_SOCKET_FILTER;
printf("%d, %s, %s\n", $r, $r, CUSTOM_ENUM);
exit();
}

yields:

6, SKB_DROP_REASON_SOCKET_FILTER, CUSTOM_ENUM

Colors are supported too, using standard terminal escape sequences:

print("\033[31mRed\t\033[33mYellow\033[0m\n")

probe

  • string probe()
  • string probe

Name of the fully expanded probe

For example: kprobe:do_nanosleep

pton

  • char addr[4] pton(const string *addr_v4)
  • char addr[16] pton(const string *addr_v6)

compile time

pton converts a text representation of an IPv4 or IPv6 address to byte array. pton infers the address family based on . or : in the given argument. pton comes in handy when we need to select packets with certain IP addresses.

rand

  • uint32 rand()
  • uint32 rand

Get a pseudo random number

This utilizes the BPF helper get_prandom_u32

reg

  • uint64 reg(const string name)

Supported probes

  • kprobe
  • uprobe

Get the contents of the register identified by name. Valid names depend on the CPU architecture.

retval

  • uint64 retval()
  • uint64 retval

Value returned by the function being traced

(kretprobe, uretprobe, fexit) For kretprobe and uretprobe, its type is uint64, but for fexit it depends. You can look up the type using bpftrace -lv

signal

  • void signal(const string sig)
  • void signal(uint32 signum)

unsafe

Kernel 5.3

This utilizes the BPF helper bpf_send_signal

Probe types: k(ret)probe, u(ret)probe, USDT, profile

Send a signal to the process being traced. The signal can either be identified by name, e.g. SIGSTOP or by ID, e.g. 19 as found in kill -l.

kprobe:__x64_sys_execve
/comm == "bash"/ {
signal(5);
}
$ ls
Trace/breakpoint trap (core dumped)

sizeof

  • uint64 sizeof(TYPE)
  • uint64 sizeof(EXPRESSION)

compile time

Returns size of the argument in bytes. Similar to C/C++ sizeof operator. Note that the expression does not get evaluated.

skboutput

  • uint32 skboutput(const string path, struct sk_buff *skb, uint64 length, const uint64 offset)

Kernel 5.5

This utilizes the BPF helper bpf_skb_output

Write sk_buff skb 's data section to a PCAP file in the path, starting from offset to offset + length.

The PCAP file is encapsulated in RAW IP, so no ethernet header is included. The data section in the struct skb may contain ethernet header in some kernel contexts, you may set offset to 14 bytes to exclude ethernet header.

Each packet’s timestamp is determined by adding nsecs and boot time, the accuracy varies on different kernels, see nsecs.

This function returns 0 on success, or a negative error in case of failure.

Environment variable BPFTRACE_PERF_RB_PAGES should be increased in order to capture large packets, or else these packets will be dropped.

Usage

# cat dump.bt
fentry:napi_gro_receive {
$ret = skboutput("receive.pcap", args.skb, args.skb->len, 0);
}

fentry:dev_queue_xmit {
// setting offset to 14, to exclude ethernet header
$ret = skboutput("output.pcap", args.skb, args.skb->len, 14);
printf("skboutput returns %d\n", $ret);
}

# export BPFTRACE_PERF_RB_PAGES=1024
# bpftrace dump.bt
...

# tcpdump -n -r ./receive.pcap | head -3
reading from file ./receive.pcap, link-type RAW (Raw IP)
dropped privs to tcpdump
10:23:44.674087 IP 22.128.74.231.63175 > 192.168.0.23.22: Flags [.], ack 3513221061, win 14009, options [nop,nop,TS val 721277750 ecr 3115333619], length 0
10:23:45.823194 IP 100.101.2.146.53 > 192.168.0.23.46619: 17273 0/1/0 (130)
10:23:45.823229 IP 100.101.2.146.53 > 192.168.0.23.46158: 45799 1/0/0 A 100.100.45.106 (60)
  • uint64 socket_cookie(struct sock *sk)

This utilizes the BPF helper bpf_get_socket_cookie

Retrieve the cookie (generated by the kernel) of the socket. If no cookie has been set yet, generate a new cookie. Once generated, the socket cookie remains stable for the life of the socket.

This function returns a uint64 unique number on success, or 0 if sk is NULL.

fentry:tcp_rcv_established
{
$cookie = socket_cookie(args->sk);
@psize[$cookie] = hist(args->skb->len);
}

Prints:

@psize[65551]:
[32, 64) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

@psize[504]:
[32, 64) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[64, 128) 1 |@@@@@@@@@@@@@ |
[128, 256) 0 | |
[256, 512) 1 |@@@@@@@@@@@@@ |
[512, 1K) 0 | |
[1K, 2K) 0 | |
[2K, 4K) 1 |@@@@@@@@@@@@@ |

str

  • string str(char * data [, uint32 length)

This utilizes the BPF helpers probe_read_str, probe_read_{kernel,user}_str

str reads a NULL terminated (\0) string from data. The maximum string length is limited by the BPFTRACE_MAX_STRLEN env variable, unless length is specified and shorter than the maximum. In case the string is longer than the specified length only length - 1 bytes are copied and a NULL byte is appended at the end.

When available (starting from kernel 5.5, see the --info flag) bpftrace will automatically use the kernel or user variant of probe_read_{kernel,user}_str based on the address space of data, see Address-spaces for more information.

strcontains

  • int64 strcontains(const char *haystack, const char *needle)

strcontains compares whether the string haystack contains the string needle. If needle is contained 1 is returned, else zero is returned.

bpftrace doesn’t read past the length of the shortest string.

strerror

  • strerror_t strerror(int error)

Convert errno code to string. This is done asynchronously in userspace when the strerror value is printed, hence the returned value can only be used for printing.

#include <errno.h>
BEGIN {
print(strerror(EPERM));
}

strftime

  • timestamp strftime(const string fmt, int64 timestamp_ns)

async

Format the nanoseconds since boot timestamp timestamp_ns according to the format specified by fmt. The time conversion and formatting happens in user space, therefore the timestamp value returned can only be used for printing using the %s format specifier.

bpftrace uses the strftime(3) function for formatting time and supports the same format specifiers.

interval:s:1 {
printf("%s\n", strftime("%H:%M:%S", nsecs));
}

bpftrace also supports the following format string extensions:

SpecifierDescription
%fMicrosecond as a decimal number, zero-padded on the left

strncmp

  • int64 strncmp(char * s1, char * s2, int64 n)

strncmp compares up to n characters string s1 and string s2. If they’re equal 0 is returned, else a non-zero value is returned.

bpftrace doesn’t read past the length of the shortest string.

The use of the == and != operators is recommended over calling strncmp directly.

system

  • void system(string namefmt [, ...args])

unsafe async

system lets bpftrace run the specified command (fork and exec) until it completes and print its stdout. The command is run with the same privileges as bpftrace and it blocks execution of the processing threads which can lead to missed events and delays processing of async events.

interval:s:1 {
time("%H:%M:%S: ");
printf("%d\n", @++);
}
interval:s:10 {
system("/bin/sleep 10");
}
interval:s:30 {
exit();
}

Note how the async time and printf first print every second until the interval:s:10 probe hits, then they print every 10 seconds due to bpftrace blocking on sleep.

Attached 3 probes
08:50:37: 0
08:50:38: 1
08:50:39: 2
08:50:40: 3
08:50:41: 4
08:50:42: 5
08:50:43: 6
08:50:44: 7
08:50:45: 8
08:50:46: 9
08:50:56: 10
08:50:56: 11
08:50:56: 12
08:50:56: 13
08:50:56: 14
08:50:56: 15
08:50:56: 16
08:50:56: 17
08:50:56: 18
08:50:56: 19

system supports the same format string and arguments that printf does.

tracepoint:syscalls:sys_enter_execve {
system("/bin/grep %s /proc/%d/status", "vmswap", pid);
}

tid

  • uint32 tid([curr_ns|init])
  • uint32 tid

Returns the thread ID of the current thread. Defaults to curr_ns.

  • tid(curr_ns) - The thread ID as seen from the PID namespace of bpftrace.
  • tid(init) - The thread ID as seen from the initial PID namespace.

time

  • void time(const string fmt)

async

Format the current wall time according to the format specifier fmt and print it to stdout. Unlike strftime() time() doesn’t send a timestamp from the probe, instead it is the time at which user-space processes the event.

bpftrace uses the strftime(3) function for formatting time and supports the same format specifiers.

uaddr

  • T * uaddr(const string sym)

Supported probes

  • uprobes
  • uretprobes
  • USDT

Does not work with ASLR, see issue #75

The uaddr function returns the address of the specified symbol. This lookup happens during program compilation and cannot be used dynamically.

The default return type is uint64*. If the ELF object size matches a known integer size (1, 2, 4 or 8 bytes) the return type is modified to match the width (uint8*, uint16*, uint32* or uint64* resp.). As ELF does not contain type info the type is always assumed to be unsigned.

uprobe:/bin/bash:readline {
printf("PS1: %s\n", str(*uaddr("ps1_prompt")));
}

uid

  • uint64 uid()
  • uint64 uid

User ID of the current thread, as seen from the init namespace

This utilizes the BPF helper get_current_uid_gid

unwatch

  • void unwatch(void * addr)

async

Removes a watchpoint

uptr

  • T * uptr(T * ptr)

Marks ptr as a user address space pointer. See the address-spaces section for more information on address-spaces. The pointer type is left unchanged.

usermode

  • uint8 usermode()
  • uint8 usermode

Returns 1 if the current process is in user mode, 0 otherwise

Currently only available on x86_64.

username

  • string username()
  • string username

Get the current username

Often this is just "root"

ustack

  • ustack_t ustack([StackMode mode, ][int limit])

These are implemented using BPF stack maps.

kprobe:do_sys_open /comm == "bash"/ { @[ustack()] = count(); }

/*
* Sample output:
* @[
* __open_nocancel+65
* command_word_completion_function+3604
* rl_completion_matches+370
* bash_default_completion+540
* attempt_shell_completion+2092
* gen_completion_matches+82
* rl_complete_internal+288
* rl_complete+145
* _rl_dispatch_subseq+647
* _rl_dispatch+44
* readline_internal_char+479
* readline_internal_charloop+22
* readline_internal+23
* readline+91
* yy_readline_get+152
* yy_readline_get+429
* yy_getc+13
* shell_getc+469
* read_token+251
* yylex+192
* yyparse+777
* parse_command+126
* read_command+207
* reader_loop+391
* main+2409
* __libc_start_main+231
* 0x61ce258d4c544155
* ]: 9
*/

Sampling only three frames from the stack (limit = 3):

kprobe:ip_output { @[ustack(3)] = count(); }

/*
* Sample output:
* @[
* __open_nocancel+65
* command_word_completion_function+3604
* rl_completion_matches+370
* ]: 20
*/

You can also choose a different output format. Available formats are bpftrace, perf, and raw (no symbolication):

kprobe:ip_output { @[ustack(perf, 3)] = count(); }

/*
* Sample output:
* @[
* 5649feec4090 readline+0 (/home/mmarchini/bash/bash/bash)
* 5649fee2bfa6 yy_readline_get+451 (/home/mmarchini/bash/bash/bash)
* 5649fee2bdc6 yy_getc+13 (/home/mmarchini/bash/bash/bash)
* ]: 20
*/

Note that for these examples to work, bash had to be recompiled with frame pointers.

usym

  • usym_t usym(uint64 * addr)

async

Supported probes

  • uprobes
  • uretprobes

Equal to ksym but resolves user space symbols.

If ASLR is enabled, user space symbolication only works when the process is running at either the time of the symbol resolution or the time of the probe attachment. The latter requires BPFTRACE_CACHE_USER_SYMBOLS to be set to PER_PID, and might not work with older versions of BCC. A similar limitation also applies to dynamically loaded symbols.

uprobe:/bin/bash:readline
{
printf("%s\n", usym(reg("ip")));
}

/*
* Sample output:
* readline
*/

zero

  • void zero(map m)

async

Set all values (for all keys) in the map to zero.

Map Value Functions

Map value functions can only be assigned to maps (when scalar) or map keys. The data types associated with these functions are only for internal use but many can be cast to integers (e.g. count_t and sum_t).

avg

  • avg_t avg(int64 n)

Calculate the running average of n between consecutive calls.

interval:s:1 {
@x++;
@y = avg(@x);
print(@x);
print(@y);
}

Internally this keeps two values in the map: value count and running total. The average is computed in user-space when printing by dividing the total by the count. However, you can get the average in kernel space in expressions like if (@y == 5) but this is expensive as bpftrace needs to iterate over all the cpus to collect and sum BOTH count and total.

count

  • count_t count()

Count how often this function is called.

Using @=count() is conceptually similar to @++. The difference is that the count() function uses a map type optimized for performance and correctness using cheap, thread-safe writes (PER_CPU). However, sync reads can be expensive as bpftrace needs to iterate over all the cpus to collect and sum these values.

Note: This differs from "raw" writes (e.g. @++) where multiple writers to a shared location might lose updates, as bpftrace does not generate any atomic instructions for ++.

Example one:

BEGIN {
@ = count();
@ = count();
printf("%d\n", (int64)@); // prints 2
exit();
}

Example two:

interval:ms:100 {
@ = count();
}

interval:s:10 {
// async read
print(@);
// sync read
if (@ > 10) {
print(("hello"));
}
clear(@);
}

hist

  • hist_t hist(int64 n[, int k])

Create a log2 histogram of n using $2^k$ buckets per power of 2, 0 <= k <= 5, defaults to 0.

kretprobe:vfs_read {
@bytes = hist(retval);
}

Prints:

@:
[1M, 2M) 3 | |
[2M, 4M) 2 | |
[4M, 8M) 2 | |
[8M, 16M) 6 | |
[16M, 32M) 16 | |
[32M, 64M) 27 | |
[64M, 128M) 48 |@ |
[128M, 256M) 98 |@@@ |
[256M, 512M) 191 |@@@@@@ |
[512M, 1G) 394 |@@@@@@@@@@@@@ |
[1G, 2G) 820 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ |

lhist

  • lhist_t lhist(int64 n, int64 min, int64 max, int64 step)

Create a linear histogram of n. lhist creates M ((max - min) / step) buckets in the range [min,max) where each bucket is step in size. Values in the range (-inf, min) and (max, inf) get their get their own bucket too, bringing the total amount of buckets created to M+2.

interval:ms:1 {
@ = lhist(rand %10, 0, 10, 1);
}

interval:s:5 {
exit();
}

Prints:

@:
[0, 1) 306 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[1, 2) 284 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[2, 3) 294 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[3, 4) 318 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[4, 5) 311 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[5, 6) 362 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[6, 7) 336 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[7, 8) 326 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[8, 9) 328 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[9, 10) 318 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |

max

  • max_t max(int64 n)

Update the map with n if n is bigger than the current value held. Similar to count this uses a PER_CPU map (thread-safe, fast writes, slow reads).

Note: this is different than the typical userspace max() in that bpftrace’s max() only takes a single argument. The logical "other" argument to compare to is the value in the map the "result" is being assigned to.

For example, compare the two logically equivalent samples (C++ vs bpftrace):

In C++:

int x = std::max(3, 33);  // x contains 33

In bpftrace:

@x = max(3);
@x = max(33); // @x contains 33

Also note that bpftrace takes care to handle the unset case. In other words, there is no default value. The first value you pass to max() will always be returned.

min

  • min_t min(int64 n)

Update the map with n if n is smaller than the current value held. Similar to count this uses a PER_CPU map (thread-safe, fast writes, slow reads).

See max() above for how this differs from the typical userspace min().

stats

  • stats_t stats(int64 n)

stats combines the count, avg and sum calls into one.

kprobe:vfs_read {
@bytes[comm] = stats(arg2);
}
@bytes[bash]: count 7, average 1, total 7
@bytes[sleep]: count 5, average 832, total 4160
@bytes[ls]: count 7, average 886, total 6208
@

sum

  • sum_t sum(int64 n)

Calculate the sum of all n passed.

Using @=sum(5) is conceptually similar to @+=5. The difference is that the sum() function uses a map type optimized for performance and correctness using cheap, thread-safe writes (PER_CPU). However, sync reads can be expensive as bpftrace needs to iterate over all the cpus to collect and sum these values.

Note: This differs from "raw" writes (e.g. @+=5) where multiple writers to a shared location might lose updates, as bpftrace does not generate any implicit atomic operations.

Example one:

BEGIN {
@ = sum(5);
@ = sum(6);
printf("%d\n", (int64)@); // prints 11
clear(@);
exit();
}

Example two:

interval:ms:100 {
@ = sum(5);
}

interval:s:10 {
// async read
print(@);
// sync read
if (@ > 10) {
print(("hello"));
}
clear(@);
}

tseries

  • tseries_t tseries(int64 n, int64 interval_ns, int64 num_intervals)
  • tseries_t tseries(int64 n, int64 interval_ns, int64 num_intervals, const string agg)

Create a time series that tracks an integer value. tseries records up to num_intervals intervals representing interval_ns nanoseconds.

Durations

interval_ns is an unsigned integer that specifies the interval duration. You may use numbers with duration suffixes to improve readability:

@a = tseries(1, 100ns, 5); // 100 nanoseconds
@b = tseries(1, 100us, 5); // 100 microseconds
@c = tseries(1, 100ms, 5); // 100 milliseconds
@d = tseries(1, 1s, 5); // 1 second

Aggregation Functions

By default, each interval in tseries contains the last value recorded in that interval. The optional agg parameter specifies how values in the same interval are aggregated.

Aggregation FunctionExampleDescription
avg@ = tseries(@v, 1s, 5, "avg")Calculate the running average of all the values in each interval.
max@ = tseries(@v, 1s, 5, "max")Calculate the maximum of all values in each interval.
min@ = tseries(@v, 1s, 5, "min")Calculate the minimum of all values in each interval.
sum@ = tseries(@v, 1s, 5, "sum")Calculate the sum of all values in each interval.

Examples

Example one:

// Record the minimum of ten random values generated during each 100ms interval.
i:ms:10 {
@ = tseries(rand % 10, 100ms, 20, "min");
}
Attached 2 probes


@:
0 2
hh:mm:ss.ms |___________________________________________________|
10:41:46.700 * | 0
10:41:46.800 * | 0
10:41:46.900 | * | 1
10:41:47.000 | * 2
10:41:47.100 | * | 1
10:41:47.200 * | 0
10:41:47.300 | * | 1
10:41:47.400 * | 0
10:41:47.500 | * | 1
10:41:47.600 * | 0
10:41:47.700 | * 2
10:41:47.800 * | 0
10:41:47.900 * | 0
10:41:48.000 | * | 1
10:41:48.100 | * | 1
10:41:48.200 | * | 1
10:41:48.300 * | 0
10:41:48.400 | * 2
10:41:48.500 | * | 1
10:41:48.600 | * | 1
v___________________________________________________v
0 2

Example two:

// Create a zigzag pattern
BEGIN {
@dir = 1;
@c = -5;
}

i:ms:100 {
@ = tseries(@c, 100ms, 20);
@c += @dir;

if (@c > 5) {
@dir = -1;
@c = 4
} else if (@c < -5) {
@dir = 1;
@c = -4;
}
}
Attached 2 probes


@:
-5 5
hh:mm:ss.ms |___________________________________________________|
10:39:49.300 * . | -5
10:39:49.400 | * . | -4
10:39:49.500 | * . | -3
10:39:49.600 | * . | -2
10:39:49.700 | * . | -1
10:39:49.800 | * | 0
10:39:49.900 | . * | 1
10:39:50.000 | . * | 2
10:39:50.100 | . * | 3
10:39:50.200 | . * | 4
10:39:50.300 | . * 5
10:39:50.400 | . * | 4
10:39:50.500 | . * | 3
10:39:50.600 | . * | 2
10:39:50.700 | . * | 1
10:39:50.800 | * | 0
10:39:50.900 | * . | -1
10:39:51.000 | * . | -2
10:39:51.100 | * . | -3
10:39:51.200 | * . | -4
v___________________________________________________v
-5 5

Invocation Mode

There are three invocation modes for bpftrace built-in functions.

ModeDescriptionExample functions
SynchronousThe value/effect of the built-in function is determined/handled right away by the bpf program in the kernel space.reg(), str(), ntop()
AsynchronousThe value/effect of the built-in function is determined/handled later by the bpftrace process in the user space.printf(), clear(), exit()
Compile-timeThe value of the built-in function is determined before bpf programs are running.kaddr(), cgroupid(), offsetof()

While BPF in the kernel can do a lot there are still things that can only be done from user space, like the outputting (printing) of data. The way bpftrace handles this is by sending events from the BPF program which user-space will pick up some time in the future (usually in milliseconds). Operations that happen in the kernel are 'synchronous' ('sync') and those that are handled in user space are 'asynchronous' ('async')

The asynchronous behaviour can lead to some unexpected behavior as updates can happen before user space had time to process the event. The following situations may occur:

  • event loss: when using printf(), the amount of data printed may be less than the actual number of events generated by the kernel during BPF program’s execution.
  • delayed exit: when using the exit() to terminate the program, bpftrace needs to handle the exit signal asynchronously causing the BPF program may continue to run for some additional time.

One example is updating a map value in a tight loop:

BEGIN {
@=0;
unroll(10) {
print(@);
@++;
}
exit()
}

Maps are printed by reference not by value and as the value gets updated right after the print user-space will likely only see the final value once it processes the event:

@: 10
@: 10
@: 10
@: 10
@: 10
@: 10
@: 10
@: 10
@: 10
@: 10

Therefore, when you need precise event statistics, it is recommended to use synchronous functions (e.g. count() and hist()) to ensure more reliable and accurate results.