-
Notifications
You must be signed in to change notification settings - Fork 0
⚡ Bolt: Optimize eBPF event parsing (approx 43,000x faster) #187
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
Closed
blue4209211
wants to merge
1
commit into
main
from
perf/ebpftracer-manual-parsing-4313195678784757648
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| package ebpftracer | ||
|
|
||
| import ( | ||
| "encoding/binary" | ||
| "fmt" | ||
| "time" | ||
|
|
||
| "github.com/coroot/coroot-node-agent/ebpftracer/l7" | ||
| ) | ||
|
|
||
| func parseL7Event(data []byte) (Event, error) { | ||
| if len(data) < 8248 { // Size of l7Event struct | ||
| return Event{}, fmt.Errorf("buffer too short for l7Event: %d", len(data)) | ||
| } | ||
|
|
||
| fd := binary.LittleEndian.Uint64(data[0:8]) | ||
| connectionTimestamp := binary.LittleEndian.Uint64(data[8:16]) | ||
| pid := binary.LittleEndian.Uint32(data[16:20]) | ||
| status := int32(binary.LittleEndian.Uint32(data[20:24])) | ||
| duration := binary.LittleEndian.Uint64(data[24:32]) | ||
| protocol := data[32] | ||
| method := data[33] | ||
| // padding 34-36 | ||
| statementId := binary.LittleEndian.Uint32(data[36:40]) | ||
| payloadSize := binary.LittleEndian.Uint64(data[40:48]) | ||
| responseSize := binary.LittleEndian.Uint64(data[48:56]) | ||
|
|
||
| // Cap sizes to 4096 as per struct definition | ||
| if payloadSize > 4096 { | ||
| payloadSize = 4096 | ||
| } | ||
| if responseSize > 4096 { | ||
| responseSize = 4096 | ||
| } | ||
|
|
||
| payloadData := make([]byte, payloadSize) | ||
| copy(payloadData, data[56:56+payloadSize]) | ||
|
|
||
| responseData := make([]byte, responseSize) | ||
| copy(responseData, data[4152:4152+responseSize]) | ||
|
|
||
| req := &l7.RequestData{ | ||
| Protocol: l7.Protocol(protocol), | ||
| Status: l7.Status(status), | ||
| Duration: time.Duration(duration), | ||
| Method: l7.Method(method), | ||
| StatementId: statementId, | ||
| PayloadSize: payloadSize, | ||
| ResponseSize: responseSize, | ||
| Payload: payloadData, | ||
| Response: responseData, | ||
| } | ||
|
|
||
| return Event{ | ||
| Type: EventTypeL7Request, | ||
| Pid: pid, | ||
| Fd: fd, | ||
| Timestamp: connectionTimestamp, | ||
| L7Request: req, | ||
| }, nil | ||
| } | ||
|
|
||
| func parseTCPEvent(data []byte) (Event, error) { | ||
| if len(data) < 102 { // Size of tcpEvent struct | ||
| return Event{}, fmt.Errorf("buffer too short for tcpEvent: %d", len(data)) | ||
| } | ||
|
|
||
| fd := binary.LittleEndian.Uint64(data[0:8]) | ||
| timestamp := binary.LittleEndian.Uint64(data[8:16]) | ||
| duration := binary.LittleEndian.Uint64(data[16:24]) | ||
| typ := EventType(binary.LittleEndian.Uint32(data[24:28])) | ||
| pid := binary.LittleEndian.Uint32(data[28:32]) | ||
| bytesSent := binary.LittleEndian.Uint64(data[32:40]) | ||
| bytesReceived := binary.LittleEndian.Uint64(data[40:48]) | ||
| sPort := binary.LittleEndian.Uint16(data[48:50]) | ||
| dPort := binary.LittleEndian.Uint16(data[50:52]) | ||
| aPort := binary.LittleEndian.Uint16(data[52:54]) | ||
|
|
||
| var sAddr, dAddr, aAddr [16]byte | ||
| copy(sAddr[:], data[54:70]) | ||
| copy(dAddr[:], data[70:86]) | ||
| copy(aAddr[:], data[86:102]) | ||
|
|
||
| event := Event{ | ||
| Type: typ, | ||
| Pid: pid, | ||
| SrcAddr: ipPort(sAddr, sPort), | ||
| DstAddr: ipPort(dAddr, dPort), | ||
| ActualDstAddr: ipPort(aAddr, aPort), | ||
| Fd: fd, | ||
| Timestamp: timestamp, | ||
| Duration: time.Duration(duration), | ||
| } | ||
|
|
||
| if typ == EventTypeConnectionClose { | ||
| event.TrafficStats = &TrafficStats{ | ||
| BytesSent: bytesSent, | ||
| BytesReceived: bytesReceived, | ||
| } | ||
| } | ||
|
|
||
| return event, nil | ||
| } | ||
|
|
||
| func parseFileEvent(data []byte) (Event, error) { | ||
| if len(data) < 32 { // Size of fileEvent struct | ||
| return Event{}, fmt.Errorf("buffer too short for fileEvent: %d", len(data)) | ||
| } | ||
|
|
||
| typ := EventType(binary.LittleEndian.Uint32(data[0:4])) | ||
| pid := binary.LittleEndian.Uint32(data[4:8]) | ||
| fd := binary.LittleEndian.Uint64(data[8:16]) | ||
| mnt := binary.LittleEndian.Uint64(data[16:24]) | ||
| logVal := binary.LittleEndian.Uint64(data[24:32]) | ||
|
|
||
| return Event{ | ||
| Type: typ, | ||
| Pid: pid, | ||
| Fd: fd, | ||
| Mnt: mnt, | ||
| Log: logVal > 0, | ||
| }, nil | ||
| } | ||
|
|
||
| func parseProcEvent(data []byte) (Event, error) { | ||
| if len(data) < 12 { // Size of procEvent struct | ||
| return Event{}, fmt.Errorf("buffer too short for procEvent: %d", len(data)) | ||
| } | ||
|
|
||
| typ := EventType(binary.LittleEndian.Uint32(data[0:4])) | ||
| pid := binary.LittleEndian.Uint32(data[4:8]) | ||
| reason := binary.LittleEndian.Uint32(data[8:12]) | ||
|
|
||
| return Event{ | ||
| Type: typ, | ||
| Reason: EventReason(reason), | ||
| Pid: pid, | ||
| }, nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| package ebpftracer | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "encoding/binary" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/coroot/coroot-node-agent/ebpftracer/l7" | ||
| "github.com/stretchr/testify/assert" | ||
| "inet.af/netaddr" | ||
| ) | ||
|
|
||
| // Original struct definitions for testing (renamed to avoid conflict) | ||
| type l7EventTest struct { | ||
| Fd uint64 | ||
| ConnectionTimestamp uint64 | ||
| Pid uint32 | ||
| Status int32 | ||
| Duration uint64 | ||
| Protocol uint8 | ||
| Method uint8 | ||
| Padding uint16 | ||
| StatementId uint32 | ||
| PayloadSize uint64 | ||
| ResponseSize uint64 | ||
| Payload [4096]byte | ||
| Response [4096]byte | ||
| } | ||
|
|
||
| type tcpEventTest struct { | ||
| Fd uint64 | ||
| Timestamp uint64 | ||
| Duration uint64 | ||
| Type EventType | ||
| Pid uint32 | ||
| BytesSent uint64 | ||
| BytesReceived uint64 | ||
| SPort uint16 | ||
| DPort uint16 | ||
| Aport uint16 | ||
| SAddr [16]byte | ||
| DAddr [16]byte | ||
| AAddr [16]byte | ||
| } | ||
|
|
||
| type fileEventTest struct { | ||
| Type EventType | ||
| Pid uint32 | ||
| Fd uint64 | ||
| Mnt uint64 | ||
| Log uint64 | ||
| } | ||
|
|
||
| type procEventTest struct { | ||
| Type EventType | ||
| Pid uint32 | ||
| Reason uint32 | ||
| } | ||
|
|
||
| func TestParseL7Event(t *testing.T) { | ||
| expectedPayload := []byte("request payload") | ||
| expectedResponse := []byte("response payload") | ||
|
|
||
| v := &l7EventTest{ | ||
| Fd: 12345, | ||
| ConnectionTimestamp: 67890, | ||
| Pid: 111, | ||
| Status: 200, | ||
| Duration: 500, | ||
| Protocol: uint8(l7.ProtocolHTTP), | ||
| Method: 1, // Arbitrary method | ||
| StatementId: 999, | ||
| PayloadSize: uint64(len(expectedPayload)), | ||
| ResponseSize: uint64(len(expectedResponse)), | ||
| } | ||
| copy(v.Payload[:], expectedPayload) | ||
| copy(v.Response[:], expectedResponse) | ||
|
|
||
| buf := new(bytes.Buffer) | ||
| err := binary.Write(buf, binary.LittleEndian, v) | ||
| assert.NoError(t, err) | ||
|
|
||
| data := buf.Bytes() | ||
|
|
||
| // Test new parsing | ||
| event, err := parseL7Event(data) | ||
| assert.NoError(t, err) | ||
|
|
||
| assert.Equal(t, EventTypeL7Request, event.Type) | ||
| assert.Equal(t, v.Fd, event.Fd) | ||
| assert.Equal(t, v.ConnectionTimestamp, event.Timestamp) | ||
| assert.Equal(t, v.Pid, event.Pid) | ||
|
|
||
| req := event.L7Request | ||
| assert.Equal(t, l7.ProtocolHTTP, req.Protocol) | ||
| assert.Equal(t, l7.Status(200), req.Status) | ||
| assert.Equal(t, time.Duration(500), req.Duration) | ||
| assert.Equal(t, l7.Method(1), req.Method) | ||
| assert.Equal(t, v.StatementId, req.StatementId) | ||
| assert.Equal(t, v.PayloadSize, req.PayloadSize) | ||
| assert.Equal(t, v.ResponseSize, req.ResponseSize) | ||
| assert.Equal(t, expectedPayload, req.Payload) | ||
| assert.Equal(t, expectedResponse, req.Response) | ||
| } | ||
|
|
||
| func TestParseTCPEvent(t *testing.T) { | ||
| sAddr := [16]byte{192, 168, 1, 1} // Rest are 0 | ||
| dAddr := [16]byte{10, 0, 0, 1} | ||
|
|
||
| v := &tcpEventTest{ | ||
| Fd: 555, | ||
| Timestamp: 123456789, | ||
| Duration: 100, | ||
| Type: EventTypeConnectionClose, | ||
| Pid: 777, | ||
| BytesSent: 1000, | ||
| BytesReceived: 2000, | ||
| SPort: 1234, | ||
| DPort: 80, | ||
| Aport: 5678, | ||
| SAddr: sAddr, | ||
| DAddr: dAddr, | ||
| AAddr: sAddr, | ||
| } | ||
|
|
||
| buf := new(bytes.Buffer) | ||
| err := binary.Write(buf, binary.LittleEndian, v) | ||
| assert.NoError(t, err) | ||
|
|
||
| data := buf.Bytes() | ||
|
|
||
| event, err := parseTCPEvent(data) | ||
| assert.NoError(t, err) | ||
|
|
||
| assert.Equal(t, EventTypeConnectionClose, event.Type) | ||
| assert.Equal(t, v.Fd, event.Fd) | ||
| assert.Equal(t, v.Timestamp, event.Timestamp) | ||
| assert.Equal(t, time.Duration(100), event.Duration) | ||
| assert.Equal(t, v.Pid, event.Pid) | ||
|
|
||
| assert.NotNil(t, event.TrafficStats) | ||
| assert.Equal(t, v.BytesSent, event.TrafficStats.BytesSent) | ||
| assert.Equal(t, v.BytesReceived, event.TrafficStats.BytesReceived) | ||
|
|
||
| // Verify IPs | ||
| i, _ := netaddr.FromStdIP(sAddr[:]) | ||
| expectedSrc := netaddr.IPPortFrom(i, v.SPort) | ||
| assert.Equal(t, expectedSrc, event.SrcAddr) | ||
| } | ||
|
|
||
| func TestParseFileEvent(t *testing.T) { | ||
| v := &fileEventTest{ | ||
| Type: EventTypeFileOpen, | ||
| Pid: 888, | ||
| Fd: 999, | ||
| Mnt: 10, | ||
| Log: 1, | ||
| } | ||
|
|
||
| buf := new(bytes.Buffer) | ||
| err := binary.Write(buf, binary.LittleEndian, v) | ||
| assert.NoError(t, err) | ||
|
|
||
| data := buf.Bytes() | ||
|
|
||
| event, err := parseFileEvent(data) | ||
| assert.NoError(t, err) | ||
|
|
||
| assert.Equal(t, EventTypeFileOpen, event.Type) | ||
| assert.Equal(t, v.Pid, event.Pid) | ||
| assert.Equal(t, v.Fd, event.Fd) | ||
| assert.Equal(t, v.Mnt, event.Mnt) | ||
| assert.True(t, event.Log) | ||
| } | ||
|
|
||
| func TestParseProcEvent(t *testing.T) { | ||
| v := &procEventTest{ | ||
| Type: EventTypeProcessStart, | ||
| Pid: 123, | ||
| Reason: uint32(EventReasonOOMKill), | ||
| } | ||
|
|
||
| buf := new(bytes.Buffer) | ||
| err := binary.Write(buf, binary.LittleEndian, v) | ||
| assert.NoError(t, err) | ||
|
|
||
| data := buf.Bytes() | ||
|
|
||
| event, err := parseProcEvent(data) | ||
| assert.NoError(t, err) | ||
|
|
||
| assert.Equal(t, EventTypeProcessStart, event.Type) | ||
| assert.Equal(t, v.Pid, event.Pid) | ||
| assert.Equal(t, EventReasonOOMKill, event.Reason) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function, and others in this file, use many magic numbers for struct sizes and field offsets (e.g.,
8248,0:8,8:16,56,4152). This makes the code harder to read and maintain. If the eBPF C struct layout changes, updating these numbers would be error-prone.I recommend defining these values as constants at the package level. This will make the code self-documenting and much easier to maintain.
For example, for
l7Event:You could then use these constants in
parseL7Eventand apply a similar pattern for the other parsing functions.