xref: /aosp_15_r20/external/bcc/tools/rdmaucma.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1#!/usr/bin/python
2# @lint-avoid-python-3-compatibility-imports
3#
4# rdmaucma: Trace RDMA Userspace Connection Manager Access Event.
5#           For Linux, uses BCC, eBPF.
6#
7# USAGE: rdmaucma [-h]
8#
9# Copyright (c) 2023 zhenwei pi
10# Licensed under the Apache License, Version 2.0 (the "License")
11#
12# 29-MAY-2023  zhenwei pi  Created this.
13
14from __future__ import print_function
15from bcc import BPF
16from socket import inet_ntop, AF_INET, AF_INET6
17import socket, struct
18import argparse
19import ctypes
20from time import strftime
21
22# arguments
23examples = """examples:
24    ./rdmaucma            # Trace all RDMA Userspace Connection Manager Access Event
25"""
26parser = argparse.ArgumentParser(
27    description="Trace RDMA Userspace Connection Manager Access Event",
28    formatter_class=argparse.RawDescriptionHelpFormatter,
29    epilog=examples)
30parser.add_argument("-D", "--debug", action="store_true",
31    help="print BPF program before starting (for debugging purposes)")
32parser.add_argument("--ebpf", action="store_true",
33    help=argparse.SUPPRESS)
34args = parser.parse_args()
35
36# define BPF program
37bpf_text = """
38#include <linux/bpf.h>
39#include <uapi/linux/ptrace.h>
40#include <rdma/rdma_cm.h>
41
42struct ipv4_data_t {
43    u32 saddr;
44    u32 daddr;
45    u16 sport;
46    u16 dport;
47    int event;
48};
49
50BPF_PERF_OUTPUT(ipv4_events);
51
52struct ipv6_data_t {
53    unsigned __int128 saddr;
54    unsigned __int128 daddr;
55    u16 sport;
56    u16 dport;
57    int event;
58};
59
60BPF_PERF_OUTPUT(ipv6_events);
61
62int trace_ucma_event_handler(struct pt_regs *ctx,
63                             struct rdma_cm_id *cm_id,
64                             struct rdma_cm_event *event)
65{
66    struct sockaddr_storage *ss = &cm_id->route.addr.src_addr;
67
68    if (ss->ss_family == AF_INET) {
69        struct ipv4_data_t ipv4_data = { 0 };
70        struct sockaddr_in *addr4 = (struct sockaddr_in *)ss;
71        ipv4_data.sport = addr4->sin_port;
72        ipv4_data.saddr = addr4->sin_addr.s_addr;
73
74        addr4 = (struct sockaddr_in *)&cm_id->route.addr.dst_addr;
75        ipv4_data.dport = addr4->sin_port;
76        ipv4_data.daddr = addr4->sin_addr.s_addr;
77
78        ipv4_data.event = event->event;
79        ipv4_events.perf_submit(ctx, &ipv4_data, sizeof(ipv4_data));
80    } else if (ss->ss_family == AF_INET6) {
81        struct ipv6_data_t ipv6_data = { 0 };
82        struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)ss;
83        ipv6_data.sport = addr6->sin6_port;
84        bpf_probe_read_kernel(&ipv6_data.saddr, sizeof(ipv6_data.saddr), addr6->sin6_addr.in6_u.u6_addr32);
85
86        addr6 = (struct sockaddr_in6 *)&cm_id->route.addr.dst_addr;
87        ipv6_data.dport = addr6->sin6_port;
88        bpf_probe_read_kernel(&ipv6_data.daddr, sizeof(ipv6_data.daddr), addr6->sin6_addr.in6_u.u6_addr32);
89
90        ipv6_data.event = event->event;
91        ipv6_events.perf_submit(ctx, &ipv6_data, sizeof(ipv6_data));
92    } else {
93        return -EPROTONOSUPPORT;
94    }
95
96    return 0;
97}
98"""
99
100# debug/dump ebpf enable or not
101if args.debug or args.ebpf:
102    print(bpf_text)
103    if args.ebpf:
104        exit()
105
106# load BPF program
107b = BPF(text=bpf_text)
108b.attach_kprobe(event="ucma_event_handler", fn_name="trace_ucma_event_handler")
109
110# see linux/include/rdma/rdma_cm.h
111rdma_cm_event = [
112        "address resolved",
113        "address error",
114        "route resolved ",
115        "route error",
116        "connect request",
117        "connect response",
118        "connect error",
119        "unreachable",
120        "rejected",
121        "established",
122        "disconnected",
123        "device removal",
124        "multicast join",
125        "multicast error",
126        "address change",
127        "timewait exit" ]
128
129def print_ipv4_event(cpu, data, size):
130    event = b["ipv4_events"].event(data)
131
132    cm_event = "unknown event"
133    if event.event < len(rdma_cm_event):
134        cm_event = rdma_cm_event[event.event]
135
136    print("%-9s %-16s %-6s %-45s %-45s" % (strftime("%H:%M:%S").encode('ascii'),
137        cm_event, "IPv4",
138        inet_ntop(AF_INET, struct.pack("I", event.saddr)) + ":" + str(socket.ntohs(event.sport)),
139        inet_ntop(AF_INET, struct.pack("I", event.daddr)) + ":" + str(socket.ntohs(event.dport))))
140
141def print_ipv6_event(cpu, data, size):
142    event = b["ipv6_events"].event(data)
143
144    cm_event = "unknown event"
145    if event.event < len(rdma_cm_event):
146        cm_event = rdma_cm_event[event.event]
147
148    print("%-9s %-16s %-6s %-45s %-45s" % (strftime("%H:%M:%S").encode('ascii'),
149        cm_event, "IPv6",
150        inet_ntop(AF_INET6, event.saddr) + ":" + str(socket.ntohs(event.sport)),
151        inet_ntop(AF_INET6, event.daddr) + ":" + str(socket.ntohs(event.dport))))
152
153
154b["ipv4_events"].open_perf_buffer(print_ipv4_event)
155b["ipv6_events"].open_perf_buffer(print_ipv6_event)
156
157# output
158print("Tracing RDMA Userspace Connection Manager Access event... Hit Ctrl-C to end.")
159
160# address length 39 = max("2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b", "255.255.255.255")
161print("%-9s %-16s %-4s %-45s %-45s" % ("Timestamp", "Event", "Family", "Local", "Remote"))
162
163while (1):
164    try:
165        b.perf_buffer_poll()
166    except KeyboardInterrupt:
167        exit()
168