btetto 0.1.5

A tool that produces Perfetto protobuf from formatted bpftrace output.
# btetto

A tool that provides cool visualizations from formatted [bpftrace](https://github.com/bpftrace/bpftrace) output.

It currently supports (by default) [Perfetto](https://perfetto.dev/) and [flamelens](https://github.com/YS-L/flamelens).

[Rust Crate available here](https://crates.io/crates/btetto)

<center><a href="images/btetto_track_event.png"><img src="images/btetto_track_event.png" border=0 width=700></a></center>

# Usage

## Perfetto
```
$ sudo bpftrace my_script.bt -f json | btetto
Attached probes: 4
^C
Writing 149 events to trace file: bpftrace_trace.binpb
```

btetto.py produces a **bpftrace_trace.binpb** protobuf file, which can then be loaded into the [Perfetto UI](https://ui.perfetto.dev/).

## flamelens
This provides an in-terminal flamegraph visualization, which will update in real time (unless passing a file).

```
$ sudo bpftrace call_stack.bt -f json | btetto --flamegraph
Attached probes: 4
```

This requires the use of `ustack`, `kstack`, or both in the bpftrace output, e.g.
```
print(("call_stack",
    ("kstack", kstack),
    ("ustack", ustack)
));
```

You can also pass a bpftrace output file to btetto e.g.
```
btetto my_bpftrace_output
```
or
```
btetto --file my_bpftrace_output --flamegraph
```

# bpftrace Output Format
The print output from bpftrace should be tuples (in JSON format e.g. `-f json`) where the first item in the tuple is the event type and the rest of the items are key/value tuples.

[**Examples**](./example_scripts/)

## Event Types (perfetto only)
- `track_event`
- `call_stack`
- `stdout`

## Track Events (Spans) (perfetto only)

**Required Fields**:
- `name` (string)
- `ts` (timestamp)
- `type` (string - see below)

**Optional Fields**:
- `pid` (number)
- `thread_name` (string)
- `tid` (number)
- `track` (string or number)
- `track_parent` (string or number)
- `unit` (string - see below)
- `flow_id` (string or number)
- `log` (tuple - see below)

**Track Event Types**
- `BEGIN`
- `END`
- `INSTANT`
- `COUNTER`

If the field is not listed above it will get logged as an annotation on the event like "bananas" and "greeting" below. pid, tid, and thread_name also get logged as annotations by default.

```
print(("track_event",
    ("name", "page_fault_user"),
    ("type", "BEGIN"),
    ("ts", $start),
    ("pid", pid),
    ("tid", tid),
    ("thread_name", comm),
    ("bananas", 10),
    ("greeting", "hello"),
    ("log", ("WARN", "this is my log message"))
));

print(("track_event",
    ("name", "page_fault_user"),
    ("type", "END"),
    ("ts", nsecs),
    ("pid", pid),
    ("tid", tid),
    ("thread_name", comm)
));
```

### track and track_parent

These are used to name the "tracks" where these events exist. At the moment they can be nested one level, where you would provide both a `track` and a `track_parent` tuple (both strings).

If `track` is not provided, you must then provide `pid`, `tid`, and `thread_name` tuples and then these track events will go into global pid/tid "tracks".

### unit
These are for `COUNTER` type track events and can be:
- `unspecified`
- `count` (default if no "unit" is provided)
- `size_bytes`
- `time_ns`

Example:
```
print(("track_event",
    ("name", "Max Duration"),
    ("type", "COUNTER"),
    ("ts", nsecs),
    ("track", "Max Duration"),
    ("unit", "count"),
    ("counter_value", @mx)
));
```

### log

The `log` tuple is a little different in that the value is another tuple where the first field is the log level and the second field is the log message e.g. `("log", ("FATAL", "This is an error message"))`. These show up as "Android Logs" in Perfetto.

**Valid Log Levels**
- `UNSPECIFIED`
- `UNUSED`
- `VERBOSE`
- `DEBUG`
- `INFO`
- `WARN`
- `ERROR`
- `FATAL`

## Call Stack Sample (perfetto and flamelens)
These are for logging call stacks (kernel, user, or both) at specific points in time. They do not have durations.

**Required Fields**:
- `pid` (number)
- `tid` (number)
- `ts` (timestamp)
- `kstack` and/or `ustack` (array of strings)

**Optional Fields**:
- `thread_name` (string)

```
print(("call_stack",
    ("ts", nsecs),
    ("pid", pid),
    ("tid", tid),
    ("thread_name", comm),
    ("kstack", kstack),
    ("ustack", ustack)
));
```

## stdout

This just prints the value to the command line e.g.
```
BEGIN {
    print(("stdout", "Tracks the duration of page faults"));
}
```