Skip to content

Commit 8e44247

Browse files
committed
capturing Mongo queries at the eBPF level
1 parent 3aa1091 commit 8e44247

File tree

5 files changed

+77
-4
lines changed

5 files changed

+77
-4
lines changed

containers/metrics.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,15 @@ var (
7979
ebpftracer.L7ProtocolRedis: {Name: "container_redis_queries_total", Help: "Total number of outbound Redis queries"},
8080
ebpftracer.L7ProtocolMemcached: {Name: "container_memcached_queries_total", Help: "Total number of outbound Memcached queries"},
8181
ebpftracer.L7ProtocolMysql: {Name: "container_mysql_queries_total", Help: "Total number of outbound Mysql queries"},
82+
ebpftracer.L7ProtocolMongo: {Name: "container_mongo_queries_total", Help: "Total number of outbound Mongo queries"},
8283
}
8384
L7Latency = map[ebpftracer.L7Protocol]prometheus.HistogramOpts{
8485
ebpftracer.L7ProtocolHTTP: {Name: "container_http_request_duration_seconds_total", Help: "Histogram of the response time for each outbound HTTP request"},
8586
ebpftracer.L7ProtocolPostgres: {Name: "container_postgres_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Postgres query"},
8687
ebpftracer.L7ProtocolRedis: {Name: "container_redis_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Redis query"},
8788
ebpftracer.L7ProtocolMemcached: {Name: "container_memcached_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Memcached query"},
8889
ebpftracer.L7ProtocolMysql: {Name: "container_mysql_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Mysql query"},
90+
ebpftracer.L7ProtocolMongo: {Name: "container_mongo_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Mongo query"},
8991
}
9092
)
9193

ebpftracer/ebpf.go

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.

ebpftracer/ebpf/l7.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
#include "redis.c"
44
#include "memcached.c"
55
#include "mysql.c"
6+
#include "mongo.c"
67

78
#define PROTOCOL_UNKNOWN 0
89
#define PROTOCOL_HTTP 1
910
#define PROTOCOL_POSTGRES 2
1011
#define PROTOCOL_REDIS 3
1112
#define PROTOCOL_MEMCACHED 4
1213
#define PROTOCOL_MYSQL 5
14+
#define PROTOCOL_MONGO 6
1315

1416
struct l7_event {
1517
__u64 fd;
@@ -46,6 +48,7 @@ struct socket_key {
4648
struct l7_request {
4749
__u64 ns;
4850
__u8 protocol;
51+
__u8 partial;
4952
};
5053

5154
struct {
@@ -80,6 +83,7 @@ static inline __attribute__((__always_inline__))
8083
int trace_enter_write(__u64 fd, char *buf, __u64 size) {
8184
__u32 pid = bpf_get_current_pid_tgid() >> 32;
8285
struct l7_request req = {};
86+
req.partial = 0;
8387
if (is_http_request(buf)) {
8488
req.protocol = PROTOCOL_HTTP;
8589
} else if (is_postgres_query(buf, size)) {
@@ -90,6 +94,8 @@ int trace_enter_write(__u64 fd, char *buf, __u64 size) {
9094
req.protocol = PROTOCOL_MEMCACHED;
9195
} else if (is_mysql_query(buf, size)) {
9296
req.protocol = PROTOCOL_MYSQL;
97+
} else if (is_mongo_query(buf, size)) {
98+
req.protocol = PROTOCOL_MONGO;
9399
} else {
94100
return 0;
95101
}
@@ -144,6 +150,13 @@ int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
144150
e.status = parse_memcached_status(args->buf, ctx->ret);
145151
} else if (req->protocol == PROTOCOL_MYSQL) {
146152
e.status = parse_mysql_status(args->buf, ctx->ret);
153+
} else if (req->protocol == PROTOCOL_MONGO) {
154+
e.status = parse_mongo_status(args->buf, ctx->ret, req->partial);
155+
if (e.status == 1) {
156+
req->partial = 1;
157+
bpf_map_update_elem(&active_l7_requests, &k, req, BPF_ANY);
158+
return 0;
159+
}
147160
}
148161
if (e.status == 0) {
149162
return 0;

ebpftracer/ebpf/mongo.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/
2+
3+
#define MONGO_OP_COMPRESSED 2012
4+
#define MONGO_OP_MSG 2013
5+
6+
struct mongo_header {
7+
__s32 length;
8+
__s32 request_id;
9+
__s32 response_to;
10+
__s32 op_code;
11+
};
12+
13+
static __always_inline
14+
int is_mongo_query(char *buf, int buf_size) {
15+
if (buf_size < 1) {
16+
return 0;
17+
}
18+
struct mongo_header h = {};
19+
if (bpf_probe_read(&h, sizeof(h), (void *)((char *)buf)) < 0) {
20+
return 0;
21+
}
22+
if (h.response_to == 0 && (h.op_code == MONGO_OP_MSG || h.op_code == MONGO_OP_COMPRESSED)) {
23+
return 1;
24+
}
25+
return 0;
26+
}
27+
28+
static __always_inline
29+
__u32 parse_mongo_status(char *buf, int buf_size, __u8 partial) {
30+
if (partial == 0 && buf_size == 4) { //partial read
31+
return 1;
32+
}
33+
struct mongo_header h = {};
34+
if (partial) {
35+
if (bpf_probe_read(&h.response_to, sizeof(h.response_to), (void *)((char *)buf+4)) < 0) {
36+
return 0;
37+
}
38+
if (bpf_probe_read(&h.op_code, sizeof(h.op_code), (void *)((char *)buf+8)) < 0) {
39+
return 0;
40+
}
41+
} else {
42+
if (bpf_probe_read(&h, sizeof(h), (void *)((char *)buf)) < 0) {
43+
return 0;
44+
}
45+
}
46+
if (h.response_to == 0) {
47+
return 0;
48+
}
49+
if (h.op_code == MONGO_OP_MSG || h.op_code == MONGO_OP_COMPRESSED) {
50+
return 200;
51+
}
52+
return 0;
53+
}
54+
55+

ebpftracer/tracer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const (
4747
L7ProtocolRedis L7Protocol = 3
4848
L7ProtocolMemcached L7Protocol = 4
4949
L7ProtocolMysql L7Protocol = 5
50+
L7ProtocolMongo L7Protocol = 6
5051
)
5152

5253
func (p L7Protocol) String() string {
@@ -61,6 +62,8 @@ func (p L7Protocol) String() string {
6162
return "Memcached"
6263
case L7ProtocolMysql:
6364
return "Mysql"
65+
case L7ProtocolMongo:
66+
return "Mongo"
6467
}
6568
return "UNKNOWN:" + strconv.Itoa(int(p))
6669
}

0 commit comments

Comments
 (0)