data->ts = bpf_ktime_get_ns() / 1000;
}
-static int get_delay(int serial, struct data_t receiver_data) {
- int delta;
+static bool exclude_dbus_daemon(struct data_t *data) {
+ char name[] = "dbus-daemon";
+ int i;
+ // manual strcmp as we don't have it in eBPF code
+ for (i = 0; i < sizeof(name); i++)
+ if (data->comm[i] != name[i])
+ return false;
+ return true;
+}
+
+static bool check_field(const char *header, size_t pos, char field_id)
+{
+ return header[pos] == field_id;
+}
+
+static size_t next_field(const char *header, size_t pos)
+{
+ // Fields are made of meta data and value.
+ // Meta data is 32-bit long, with value's type at 3rd byte.
+ char field_type = header[pos + 2];
+ unsigned field_size;
+
+ // Fields are strings (type 's', 'g' and 'g') or uint32.
+ // Strings are made of string length (uint32) and actual string.
+ // Let's read the string size.
+ if (field_type == 's' || field_type == 'o' || field_type == 'g')
+ field_size = *(uint32_t *)(header + pos + sizeof(uint32_t));
+ else
+ field_size = sizeof(uint32_t);
+
+ pos += field_size + sizeof(uint32_t); // skip meta data and value
+ pos = (pos + 7) & ~7; // align to 8 for next field
+ return pos;
+}
+
+static const char *get_field_ptr(const char *header, size_t pos)
+{
+ // The field's type is at 3rd byte
+ char field_type = header[pos + 2];
+ const char *ret = header + pos + sizeof(uint32_t); // skip meta data
+
+ if (field_type == 's' || field_type == 'o' || field_type == 'g')
+ ret += sizeof(uint32_t); // skip string length
+ return ret;
+}
+
+static const char *get_field_value(struct DBusMessage *msg, char field_id)
+{
+ // DBUS1 marshalling
+ // TODO GVariant, for kdbus if needed
+ const char *header = msg->header.data.str;
+ size_t pos = 16; // start of the fields
+
+ // loop for 9 possible headers, unrolled manually
+ // kernel does not want to accept this loop:
+ //
+ // for (i = 0; i < 9; i++) {
+ // if (check_field(header, pos, field_id)
+ // return get_field_ptr(header, pos);
+ // pos = next_field(header, pos);
+ // }
+ //
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ pos = next_field(header, pos);
+ if (check_field(header, pos, field_id))
+ return get_field_ptr(header, pos);
+ return NULL;
+}
+
+static bool exclude_destination_org_freedesktop_DBus_and_empty(struct DBusMessage *msg)
+{
+ char name[] = "org.freedesktop.DBus";
+
+ const char *destination = get_field_value(msg, DBUS_HEADER_FIELD_DESTINATION);
+ if (destination) {
+ int i;
+ // manual strcmp as we don't have it in eBPF code
+ for (i = 0; i < sizeof(name); i++)
+ if (destination[i] != name[i])
+ return false;
+ return true;
+ }
+ return true;
+}
+
+static int get_delay(int serial, struct data_t *receiver_data) {
+ int64_t delta;
struct stats_t *stat;
struct data_t *sender_data = msg_sent_addr.lookup(&serial);
- if (sender_data != 0 && sender_data->pid != receiver_data.pid) {
+ if (sender_data != 0) {
+ struct data_t s_data = *sender_data;
msg_sent_addr.delete(&serial);
- delta = receiver_data.ts - sender_data->ts;
- if (delta > 5000 || delta <= 0) {
+ delta = receiver_data->ts - s_data.ts;
+ if (delta <= 0) {
+ bpf_trace_printk("wrong delta: %lld\n", delta);
return 0;
}
latency_histo.increment(bpf_log2l(delta));
- receiver_data.pid = sender_data->pid;
- receiver_data.ts = 0;
- bpf_probe_read(&receiver_data.comm, sizeof(receiver_data.comm), sender_data->comm);
- stat = msg_latency.lookup(&receiver_data);
+ receiver_data->pid = s_data.pid;
+ receiver_data->ts = 0;
+ bpf_probe_read(&receiver_data->comm, sizeof(receiver_data->comm), s_data.comm);
+ stat = msg_latency.lookup(receiver_data);
if (stat == 0) {
struct stats_t stats = {delta, delta, 1, delta};
- msg_latency.insert(&receiver_data, &stats);
+ msg_latency.insert(receiver_data, &stats);
return 0;
}
struct stats_t stats = {stat->min, stat->avg, stat->count, stat->max};
}
avg += delta;
stats.avg = (avg / stats.count);
- msg_latency.update(&receiver_data, &stats);
+ msg_latency.update(receiver_data, &stats);
}
return 0;
}
-int get_msg_addr(struct pt_regs *ctx, struct DBusConnection *conn, struct DBusMessage *msg){
+int get_msg_addr(struct pt_regs *ctx, struct DBusMessage *msg, const struct DBusString **h, const struct DBusString **b){
struct data_t data = {};
get_process_data(&data);
struct header_data *header = msg->header.data.str;
u32 serial = header->serial;
+ if (exclude_dbus_daemon(&data))
+ return 0;
msg_sent_addr.update(&serial,&data);
return 0;
}
-int get_msg_latency(struct pt_regs *ctx, struct DBusConnection *conn) {
+int get_msg_latency(struct pt_regs *ctx, struct DBusConnection *conn, struct DBusList *link) {
struct data_t receiver_data = {};
struct DBusMessage *msg = 0;
struct header_data *header = 0;
int serial = 0;
- struct DBusList *link = conn->incoming_messages;
if (link == 0) {
return 0;
msg = link->data;
header = msg->header.data.str;
serial = header->serial;
- get_delay(serial, receiver_data);
+
+ if (exclude_dbus_daemon(&receiver_data)) {
+ // check if message destination is org.freedesktop.DBus
+ // we don't take into account messages directed to dbus-daemon
+ if (exclude_destination_org_freedesktop_DBus_and_empty(msg))
+ msg_sent_addr.delete(&serial);
+ return 0;
+ }
+
+ get_delay(serial, &receiver_data);
return 0;
}
-int g_get_latency(struct pt_regs *ctx, struct GDBusMessage *msg) {
+int g_get_msg_addr(struct pt_regs *ctx, struct GDBusMessage *msg, void *user_data) {
struct data_t *receiver_data;
u32 serial = msg->serial;
- receiver_data = msg_sent_addr.lookup(&serial);
struct data_t data = {};
get_process_data(&data);
- data.ts = 0;
- if ((receiver_data == 0) || (receiver_data->pid == data.pid)){
- msg_sent_addr.delete(&serial);
- get_delay(serial, data);
- return 0;
- }
msg_sent_addr.update(&serial, &data);
- bpf_trace_printk("sender: %d\n", serial);
+ return 0;
+}
+
+int g_get_msg_latency(struct pt_regs *ctx, struct GDBusMessage *msg, void *user_data) {
+ struct data_t *receiver_data;
+ u32 serial = msg->serial;
+ struct data_t data = {};
+ get_process_data(&data);
+ get_delay(serial, &data);
return 0;
}