Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce BPF memory consumption #620

Merged
merged 11 commits into from
Feb 16, 2024
Merged

Conversation

grcevski
Copy link
Contributor

@grcevski grcevski commented Feb 13, 2024

This PR refactors how we manage the ringbuffer and other data structures. Upon review of our kernel memory usage, there was a lot of unnecessary allocation going on. Each bpf program had it's own ringbuffer of 16MB and we used to include headers which accidentally brought in extra unused maps.

I think there's still some technical debt left in how the tracing headers are structured on the bpf side, it's a relic from a lot of experimentation and I'll follow-up with a PR to do further clean-up.

I also combined the SQL into the core http. Seems to be loaded always because the symbols exist in the binary and it duplicates some additional maps. I'm going to separate it out again with a new refactor, such that we include only minimum headers and not duplicate data structures.

@codecov-commenter
Copy link

codecov-commenter commented Feb 13, 2024

Codecov Report

Attention: 12 lines in your changes are missing coverage. Please review.

Comparison is base (6cb90b3) 79.19% compared to head (6e38828) 79.88%.

Files Patch % Lines
pkg/internal/ebpf/common/httpfltr_transform.go 92.50% 6 Missing and 3 partials ⚠️
pkg/internal/ebpf/common/common.go 90.00% 1 Missing ⚠️
pkg/internal/ebpf/common/spanner.go 50.00% 1 Missing ⚠️
pkg/internal/ebpf/httpfltr/httpfltr.go 93.75% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #620      +/-   ##
==========================================
+ Coverage   79.19%   79.88%   +0.69%     
==========================================
  Files          70       69       -1     
  Lines        6027     5967      -60     
==========================================
- Hits         4773     4767       -6     
+ Misses       1023      970      -53     
+ Partials      231      230       -1     
Flag Coverage Δ
integration-test 69.61% <90.35%> (+0.72%) ⬆️
unittests 44.19% <46.42%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@mariomac mariomac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is interesting to optimize memory usage also in the eBPF side. However I think this couldn't affect the VMSize metrics of Beyla, as AFAIK this memory is accounted into the Kernel and not the userspace process.

@@ -21,7 +21,7 @@
// we can share them later if we find is worth not including code per duplicate
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
__uint(max_entries, 1 << 16);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As max_entries are allocated dynamically as long as more space is needed, this should not affect in terms of memory (unless we end up having more than 2^16 events in the queue).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I printed BPF metrics, I see the whole ring buffer pre-allocated in locked memory.

Before:

870: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 16777216  memlock 16,855,384B

After:

1079: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 65536  memlock 78,424B

There are more issues with what we do. One example is that we'll load two separate programs when we have gin. Once for the gin functions, but we'll load the go http too, because it's there in the binary. So in a sense, we'll duplicate the memory and have 2 ring buffers when we instrument with gin.

Another issue is grpc, golang.org/x/net/http2/hpack.(*Encoder).WriteField belongs to the gosdk, so we load both http and grpc for grpc only applications, again two ring buffers, all maps are duplicate :(.

@@ -18,7 +18,7 @@ const watch_info_t *unused_2 __attribute__((unused));

struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
__uint(max_entries, 1 << 8);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The minimum size should be multiple of 4096, according to this: https://stackoverflow.com/questions/63415220/bpf-ring-buffer-invalid-argument-22

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I will increase it, although even with 256 I see the events flowing...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! So maybe the reference link is wrong. The documentation doesn't say anything but "must be power of 2".

@grcevski
Copy link
Contributor Author

I think is interesting to optimize memory usage also in the eBPF side. However I think this couldn't affect the VMSize metrics of Beyla, as AFAIK this memory is accounted into the Kernel and not the userspace process.

Yes you are right, we do have 2 separate issues. I have to check if the container metrics report the kernel locked memory per container in the container memory or it's only per node.

@grcevski grcevski changed the title WIP: Reduce BPF memory consumption Reduce BPF memory consumption Feb 14, 2024
@grcevski grcevski requested a review from marctc February 15, 2024 23:59
Copy link
Contributor

@mariomac mariomac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing! Do we have any measurement or estimation for the improvements in memory?

&nethttp.GinTracer{Tracer: *nethttp.New(&cfg.EBPF, metrics)},
grpc.New(&cfg.EBPF, metrics),
goruntime.New(&cfg.EBPF, metrics),
gosql.New(&cfg.EBPF, metrics),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you forget to re-add gosql?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it temporarily, the whole separate file and EBPF program, because it required more headers refactor in the BPF side, so we don't include collections we don't need twice. I'm going to follow-up with a PR to split that off again after I clean up the headers more.

@grcevski
Copy link
Contributor Author

Here's some data about the memory size reductions, really demonstrated with our Go testserver app, where we bundle Go HTTP, Gin and gRPC in one:

All of this is output from sudo bpftool map show.

Before:

Total: 109,602,592 B, or 104 MB

5244: ringbuf  name watch_events  flags 0x0
	key 0B  value 0B  max_entries 16777216  memlock 16855384B
5245: array  name .rodata  flags 0x80
	key 4B  value 34B  max_entries 1  memlock 360B
	btf_id 8416  frozen
5246: lru_hash  name filtered_connec  flags 0x0
	key 40B  value 13B  max_entries 10000  memlock 1303616B
	btf_id 8418
5247: lru_hash  name pid_cache  flags 0x0
	key 4B  value 4B  max_entries 3000  memlock 259008B
	btf_id 8419
5248: lru_hash  name valid_pids  flags 0x0
	key 8B  value 1B  max_entries 3000  memlock 259008B
	btf_id 8420
5249: array  name .rodata  flags 0x80
	key 4B  value 2080B  max_entries 1  memlock 2400B
	btf_id 8421  frozen
5250: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8422
5251: lru_hash  name trace_map  flags 0x0
	key 36B  value 56B  max_entries 10000  memlock 1703616B
	btf_id 8423
5252: lru_hash  name go_trace_map  flags 0x0
	key 8B  value 48B  max_entries 10000  memlock 1303616B
	btf_id 8424
5253: hash  name ongoing_http_se  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8425
5254: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5255: hash  name ongoing_gorouti  flags 0x0
	key 8B  value 16B  max_entries 10000  memlock 985216B
	btf_id 8427
5256: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 16777216  memlock 16855384B
5257: array  name .rodata.str1.1  flags 0x80
	key 4B  value 29B  max_entries 1  memlock 352B
	frozen
5258: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8434
5259: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 200B  max_entries 1000  memlock 279136B
	btf_id 8435
5260: array  name .rodata  flags 0x80
	key 4B  value 2080B  max_entries 1  memlock 2400B
	btf_id 8442  frozen
5261: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8443
5262: hash  name ongoing_http_se  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8444
5263: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5264: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 16777216  memlock 16855384B
5265: array  name .rodata.str1.1  flags 0x80
	key 4B  value 29B  max_entries 1  memlock 352B
	frozen
5266: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8452
5267: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 200B  max_entries 1000  memlock 279136B
	btf_id 8453
5268: array  name .rodata  flags 0x80
	key 4B  value 1966B  max_entries 1  memlock 2288B
	btf_id 8460  frozen
5269: lru_hash  name ongoing_grpc_cl  flags 0x0
	key 8B  value 88B  max_entries 1000  memlock 161856B
	btf_id 8461
5270: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5271: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 16777216  memlock 16855384B
5272: array  name .rodata.str1.1  flags 0x80
	key 4B  value 52B  max_entries 1  memlock 376B
	frozen
5273: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8467
5274: hash  name ongoing_grpc_se  flags 0x0
	key 8B  value 64B  max_entries 1000  memlock 140416B
	btf_id 8468
5275: hash  name ongoing_grpc_re  flags 0x0
	key 8B  value 2B  max_entries 1000  memlock 83296B
	btf_id 8470
5276: hash  name ongoing_grpc_he  flags 0x0
	key 8B  value 88B  max_entries 1000  memlock 164896B
	btf_id 8476
5277: lru_hash  name ongoing_streams  flags 0x0
	key 4B  value 88B  max_entries 1000  memlock 161856B
	btf_id 8477
5278: array  name .rodata  flags 0x80
	key 4B  value 332B  max_entries 1  memlock 656B
	btf_id 8478  frozen
5279: hash  name newproc1  flags 0x0
	key 8B  value 8B  max_entries 1000  memlock 83296B
	btf_id 8480
5280: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 16777216  memlock 16855384B
5281: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5282: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8483
5283: array  name .rodata  flags 0x80
	key 4B  value 292B  max_entries 1  memlock 616B
	btf_id 8484  frozen
5284: hash  name ongoing_sql_que  flags 0x0
	key 8B  value 72B  max_entries 1000  memlock 148576B
	btf_id 8485
5285: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 16777216  memlock 16855384B
5286: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5287: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8488

After:

Total: 6,769,216 B, or 6.4 MB

5206: ringbuf  name watch_events  flags 0x0
	key 0B  value 0B  max_entries 4096  memlock 16744B
5207: array  name .rodata  flags 0x80
	key 4B  value 30B  max_entries 1  memlock 352B
	btf_id 8341  frozen
5208: array  name .rodata  flags 0x80
	key 4B  value 2292B  max_entries 1  memlock 2616B
	btf_id 8343  frozen
5209: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8344
5210: lru_hash  name trace_map  flags 0x0
	key 36B  value 56B  max_entries 10000  memlock 1703616B
	btf_id 8345
5211: lru_hash  name go_trace_map  flags 0x0
	key 8B  value 48B  max_entries 10000  memlock 1303616B
	btf_id 8346
5212: hash  name ongoing_http_se  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8347
5213: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5214: hash  name ongoing_gorouti  flags 0x0
	key 8B  value 16B  max_entries 10000  memlock 985216B
	btf_id 8349
5215: ringbuf  name events  flags 0x0
	key 0B  value 0B  max_entries 65536  memlock 78424B
5216: array  name .rodata.str1.1  flags 0x80
	key 4B  value 29B  max_entries 1  memlock 352B
	frozen
5217: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8356
5218: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 200B  max_entries 1000  memlock 279136B
	btf_id 8357
5219: hash  name ongoing_sql_que  flags 0x0
	key 8B  value 72B  max_entries 1000  memlock 148576B
	btf_id 8360
5220: array  name .rodata  flags 0x80
	key 4B  value 2292B  max_entries 1  memlock 2616B
	btf_id 8367  frozen
5221: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8368
5222: hash  name ongoing_http_se  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8369
5223: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5224: array  name .rodata.str1.1  flags 0x80
	key 4B  value 29B  max_entries 1  memlock 352B
	frozen
5225: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 56B  max_entries 1000  memlock 132256B
	btf_id 8377
5226: hash  name ongoing_http_cl  flags 0x0
	key 8B  value 200B  max_entries 1000  memlock 279136B
	btf_id 8378
5227: hash  name ongoing_sql_que  flags 0x0
	key 8B  value 72B  max_entries 1000  memlock 148576B
	btf_id 8381
5228: array  name .rodata  flags 0x80
	key 4B  value 1966B  max_entries 1  memlock 2288B
	btf_id 8388  frozen
5229: lru_hash  name ongoing_grpc_cl  flags 0x0
	key 8B  value 88B  max_entries 1000  memlock 161856B
	btf_id 8389
5230: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5231: array  name .rodata.str1.1  flags 0x80
	key 4B  value 52B  max_entries 1  memlock 376B
	frozen
5232: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8395
5233: hash  name ongoing_grpc_se  flags 0x0
	key 8B  value 64B  max_entries 1000  memlock 140416B
	btf_id 8396
5234: hash  name ongoing_grpc_re  flags 0x0
	key 8B  value 2B  max_entries 1000  memlock 83296B
	btf_id 8398
5235: hash  name ongoing_grpc_he  flags 0x0
	key 8B  value 88B  max_entries 1000  memlock 164896B
	btf_id 8404
5236: lru_hash  name ongoing_streams  flags 0x0
	key 4B  value 88B  max_entries 1000  memlock 161856B
	btf_id 8405
5237: array  name .rodata  flags 0x80
	key 4B  value 322B  max_entries 1  memlock 648B
	btf_id 8406  frozen
5238: hash  name newproc1  flags 0x0
	key 8B  value 8B  max_entries 1000  memlock 83296B
	btf_id 8408
5239: percpu_array  name golang_mapbucke  flags 0x0
	key 4B  value 336B  max_entries 1  memlock 7048B
5240: hash  name ongoing_http_se  flags 0x0
	key 8B  value 36B  max_entries 1000  memlock 115936B
	btf_id 8411

@grcevski grcevski merged commit e6d8170 into grafana:main Feb 16, 2024
4 checks passed
@grcevski grcevski deleted the reduce_bpf_memory branch February 16, 2024 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants