Skip to content

Commit

Permalink
Fix off-by-one for histogram bucket seams
Browse files Browse the repository at this point in the history
See #488 for more details.

With `exp2zero` histogram:

* Go:

```
$ go run cmd/derp/main.go
poc_metric_bucket{le="0"} 1
poc_metric_bucket{le="1"} 2
poc_metric_bucket{le="2"} 3
poc_metric_bucket{le="4"} 5
poc_metric_bucket{le="8"} 9
poc_metric_bucket{le="+Inf"} 9
poc_metric_sum 36
poc_metric_count 9
```

* `ebpf_exporter`:

```
ebpf_exporter_poc_exp2zero_values_bucket{le="0"} 1
ebpf_exporter_poc_exp2zero_values_bucket{le="1"} 2
ebpf_exporter_poc_exp2zero_values_bucket{le="2"} 3
ebpf_exporter_poc_exp2zero_values_bucket{le="4"} 5
ebpf_exporter_poc_exp2zero_values_bucket{le="8"} 9
ebpf_exporter_poc_exp2zero_values_bucket{le="+Inf"} 9
ebpf_exporter_poc_exp2zero_values_sum 36
ebpf_exporter_poc_exp2zero_values_count 9
```

With `exp2` histogram:

* Go:

```
$ go run cmd/derp/main.go
poc_metric_bucket{le="1"} 1
poc_metric_bucket{le="2"} 2
poc_metric_bucket{le="4"} 4
poc_metric_bucket{le="8"} 8
poc_metric_bucket{le="+Inf"} 8
poc_metric_sum 36
poc_metric_count 8
```

* `ebpf_exporter`

```
ebpf_exporter_poc_exp2_values_bucket{le="1"} 1
ebpf_exporter_poc_exp2_values_bucket{le="2"} 2
ebpf_exporter_poc_exp2_values_bucket{le="4"} 4
ebpf_exporter_poc_exp2_values_bucket{le="8"} 8
ebpf_exporter_poc_exp2_values_bucket{le="+Inf"} 8
ebpf_exporter_poc_exp2_values_sum 36
ebpf_exporter_poc_exp2_values_count 8
```
  • Loading branch information
bobrik committed Jan 20, 2025
1 parent d3032b9 commit 4fde27c
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 21 deletions.
21 changes: 2 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ for i = bucket_min; i < bucket_max; i++ {

Here `map` is the map from the kernel and `result` is what goes to prometheus.

We take cumulative `count`, because this is what prometheus expects.
Use `increment_exp2_histogram` in ebpf to observe values.

##### `exp2zero` histograms

Expand All @@ -466,12 +466,7 @@ These are the same as `exp2` histograms, except:
* The first key is for the value `0`
* All other keys are `1` larger than they should be

This is useful if your actual observed value can be zero, as regular `exp2`
histograms cannot express this due the the fact that `log2(0)` is invalid,
and in fact BPF treats `log2(0)` as `0`, and `exp2(0)` is 1, not 0.

See [`tcp-syn-backlog-exp2zero.bpf.c`](examples/tcp-syn-backlog-exp2zero.bpf.c)
for an example of a config that makes use of this.
Use `increment_exp2zero_histogram` in ebpf to observe values.

##### `linear` histograms

Expand Down Expand Up @@ -509,18 +504,6 @@ information and allowing richer metrics.
For `fixed` histograms, if `buckets_keys[len(bucket_keys) - 1 ] + 1` contains
a non-zero value, it will be used as the `sum` key.

##### Advice on values outside of `[bucket_min, bucket_max]`

For both `exp2` and `linear` histograms it is important that kernel does
not count events into buckets outside of `[bucket_min, bucket_max]` range.
If you encounter a value above your range, truncate it to be in it. You're
losing `+Inf` bucket, but usually it's not that big of a deal.

Each kernel map key must count values under that key's value to match
the behavior of prometheus. For example, `exp2` histogram key `3` should
count values for `(exp2(2), exp2(3)]` interval: `(4, 8]`. To put it simply:
use `log2l` or integer division and you'll be good.

### Labels

Labels transform kernel map keys into prometheus labels.
Expand Down
16 changes: 16 additions & 0 deletions examples/bits.bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,20 @@ static __always_inline u64 log2l(u64 v)
return log2(v);
}

// Produce values that are usable for prometheus. See:
// * https://github.com/cloudflare/ebpf_exporter/issues/488
static __always_inline u64 log2l_histogram(u64 v)
{
u64 rounded = log2l(v);

if (rounded == 0) {
return 0;
}

if (2 << (rounded - 1) == v)
return rounded;
else
return rounded + 1;
}

#endif /* __BITS_BPF_H */
4 changes: 2 additions & 2 deletions examples/maps.bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ static inline int increment_map_nosync(void *map, void *key, u64 increment)
}

#define _increment_ex2_histogram(map, key, increment, max_bucket, increment_fn) \
key.bucket = log2l(increment); \
key.bucket = log2l_histogram(increment); \
\
if (key.bucket > max_bucket) { \
key.bucket = max_bucket; \
Expand All @@ -69,7 +69,7 @@ static inline int increment_map_nosync(void *map, void *key, u64 increment)
if (increment == 0) { \
key.bucket = 0; \
} else { \
key.bucket = log2l(increment) + 1; \
key.bucket = log2l_histogram(increment) + 1; \
} \
\
_increment_histogram(map, key, increment, max_bucket, increment_fn);
Expand Down

0 comments on commit 4fde27c

Please sign in to comment.