1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) PLUMgrid, Inc. 3*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 4*387f9dfdSAndroid Build Coastguard Worker 5*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 6*387f9dfdSAndroid Build Coastguard Workerimport os 7*387f9dfdSAndroid Build Coastguard Workerimport ctypes as ct 8*387f9dfdSAndroid Build Coastguard Workerimport random 9*387f9dfdSAndroid Build Coastguard Workerimport time 10*387f9dfdSAndroid Build Coastguard Workerimport subprocess 11*387f9dfdSAndroid Build Coastguard Workerfrom unittest import main, TestCase, skipUnless 12*387f9dfdSAndroid Build Coastguard Workerfrom utils import kernel_version_ge 13*387f9dfdSAndroid Build Coastguard Worker 14*387f9dfdSAndroid Build Coastguard Workerclass TestRingbuf(TestCase): 15*387f9dfdSAndroid Build Coastguard Worker @skipUnless(kernel_version_ge(5,8), "requires kernel >= 5.8") 16*387f9dfdSAndroid Build Coastguard Worker def test_ringbuf_output(self): 17*387f9dfdSAndroid Build Coastguard Worker self.counter = 0 18*387f9dfdSAndroid Build Coastguard Worker 19*387f9dfdSAndroid Build Coastguard Worker class Data(ct.Structure): 20*387f9dfdSAndroid Build Coastguard Worker _fields_ = [("ts", ct.c_ulonglong)] 21*387f9dfdSAndroid Build Coastguard Worker 22*387f9dfdSAndroid Build Coastguard Worker def cb(ctx, data, size): 23*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(size, ct.sizeof(Data)) 24*387f9dfdSAndroid Build Coastguard Worker event = ct.cast(data, ct.POINTER(Data)).contents 25*387f9dfdSAndroid Build Coastguard Worker self.counter += 1 26*387f9dfdSAndroid Build Coastguard Worker 27*387f9dfdSAndroid Build Coastguard Worker text = b""" 28*387f9dfdSAndroid Build Coastguard WorkerBPF_RINGBUF_OUTPUT(events, 8); 29*387f9dfdSAndroid Build Coastguard Workerstruct data_t { 30*387f9dfdSAndroid Build Coastguard Worker u64 ts; 31*387f9dfdSAndroid Build Coastguard Worker}; 32*387f9dfdSAndroid Build Coastguard Workerint do_sys_nanosleep(void *ctx) { 33*387f9dfdSAndroid Build Coastguard Worker struct data_t data = {bpf_ktime_get_ns()}; 34*387f9dfdSAndroid Build Coastguard Worker events.ringbuf_output(&data, sizeof(data), 0); 35*387f9dfdSAndroid Build Coastguard Worker return 0; 36*387f9dfdSAndroid Build Coastguard Worker} 37*387f9dfdSAndroid Build Coastguard Worker""" 38*387f9dfdSAndroid Build Coastguard Worker b = BPF(text=text) 39*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"nanosleep"), 40*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 41*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"clock_nanosleep"), 42*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 43*387f9dfdSAndroid Build Coastguard Worker b[b"events"].open_ring_buffer(cb) 44*387f9dfdSAndroid Build Coastguard Worker subprocess.call(['sleep', '0.1']) 45*387f9dfdSAndroid Build Coastguard Worker b.ring_buffer_poll() 46*387f9dfdSAndroid Build Coastguard Worker self.assertGreater(self.counter, 0) 47*387f9dfdSAndroid Build Coastguard Worker b.cleanup() 48*387f9dfdSAndroid Build Coastguard Worker 49*387f9dfdSAndroid Build Coastguard Worker @skipUnless(kernel_version_ge(5,8), "requires kernel >= 5.8") 50*387f9dfdSAndroid Build Coastguard Worker def test_ringbuf_consume(self): 51*387f9dfdSAndroid Build Coastguard Worker self.counter = 0 52*387f9dfdSAndroid Build Coastguard Worker 53*387f9dfdSAndroid Build Coastguard Worker class Data(ct.Structure): 54*387f9dfdSAndroid Build Coastguard Worker _fields_ = [("ts", ct.c_ulonglong)] 55*387f9dfdSAndroid Build Coastguard Worker 56*387f9dfdSAndroid Build Coastguard Worker def cb(ctx, data, size): 57*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(size, ct.sizeof(Data)) 58*387f9dfdSAndroid Build Coastguard Worker event = ct.cast(data, ct.POINTER(Data)).contents 59*387f9dfdSAndroid Build Coastguard Worker self.counter += 1 60*387f9dfdSAndroid Build Coastguard Worker 61*387f9dfdSAndroid Build Coastguard Worker text = b""" 62*387f9dfdSAndroid Build Coastguard WorkerBPF_RINGBUF_OUTPUT(events, 8); 63*387f9dfdSAndroid Build Coastguard Workerstruct data_t { 64*387f9dfdSAndroid Build Coastguard Worker u64 ts; 65*387f9dfdSAndroid Build Coastguard Worker}; 66*387f9dfdSAndroid Build Coastguard Workerint do_sys_nanosleep(void *ctx) { 67*387f9dfdSAndroid Build Coastguard Worker struct data_t data = {bpf_ktime_get_ns()}; 68*387f9dfdSAndroid Build Coastguard Worker events.ringbuf_output(&data, sizeof(data), 0); 69*387f9dfdSAndroid Build Coastguard Worker return 0; 70*387f9dfdSAndroid Build Coastguard Worker} 71*387f9dfdSAndroid Build Coastguard Worker""" 72*387f9dfdSAndroid Build Coastguard Worker b = BPF(text=text) 73*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"nanosleep"), 74*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 75*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"clock_nanosleep"), 76*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 77*387f9dfdSAndroid Build Coastguard Worker b[b"events"].open_ring_buffer(cb) 78*387f9dfdSAndroid Build Coastguard Worker subprocess.call(['sleep', '0.1']) 79*387f9dfdSAndroid Build Coastguard Worker b.ring_buffer_consume() 80*387f9dfdSAndroid Build Coastguard Worker self.assertGreater(self.counter, 0) 81*387f9dfdSAndroid Build Coastguard Worker b.cleanup() 82*387f9dfdSAndroid Build Coastguard Worker 83*387f9dfdSAndroid Build Coastguard Worker @skipUnless(kernel_version_ge(5,8), "requires kernel >= 5.8") 84*387f9dfdSAndroid Build Coastguard Worker def test_ringbuf_submit(self): 85*387f9dfdSAndroid Build Coastguard Worker self.counter = 0 86*387f9dfdSAndroid Build Coastguard Worker 87*387f9dfdSAndroid Build Coastguard Worker class Data(ct.Structure): 88*387f9dfdSAndroid Build Coastguard Worker _fields_ = [("ts", ct.c_ulonglong)] 89*387f9dfdSAndroid Build Coastguard Worker 90*387f9dfdSAndroid Build Coastguard Worker def cb(ctx, data, size): 91*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(size, ct.sizeof(Data)) 92*387f9dfdSAndroid Build Coastguard Worker event = ct.cast(data, ct.POINTER(Data)).contents 93*387f9dfdSAndroid Build Coastguard Worker self.counter += 1 94*387f9dfdSAndroid Build Coastguard Worker 95*387f9dfdSAndroid Build Coastguard Worker text = b""" 96*387f9dfdSAndroid Build Coastguard WorkerBPF_RINGBUF_OUTPUT(events, 8); 97*387f9dfdSAndroid Build Coastguard Workerstruct data_t { 98*387f9dfdSAndroid Build Coastguard Worker u64 ts; 99*387f9dfdSAndroid Build Coastguard Worker}; 100*387f9dfdSAndroid Build Coastguard Workerint do_sys_nanosleep(void *ctx) { 101*387f9dfdSAndroid Build Coastguard Worker struct data_t *data = events.ringbuf_reserve(sizeof(struct data_t)); 102*387f9dfdSAndroid Build Coastguard Worker if (!data) 103*387f9dfdSAndroid Build Coastguard Worker return 1; 104*387f9dfdSAndroid Build Coastguard Worker data->ts = bpf_ktime_get_ns(); 105*387f9dfdSAndroid Build Coastguard Worker events.ringbuf_submit(data, 0); 106*387f9dfdSAndroid Build Coastguard Worker return 0; 107*387f9dfdSAndroid Build Coastguard Worker} 108*387f9dfdSAndroid Build Coastguard Worker""" 109*387f9dfdSAndroid Build Coastguard Worker b = BPF(text=text) 110*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"nanosleep"), 111*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 112*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"clock_nanosleep"), 113*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 114*387f9dfdSAndroid Build Coastguard Worker b[b"events"].open_ring_buffer(cb) 115*387f9dfdSAndroid Build Coastguard Worker subprocess.call(['sleep', '0.1']) 116*387f9dfdSAndroid Build Coastguard Worker b.ring_buffer_poll() 117*387f9dfdSAndroid Build Coastguard Worker self.assertGreater(self.counter, 0) 118*387f9dfdSAndroid Build Coastguard Worker b.cleanup() 119*387f9dfdSAndroid Build Coastguard Worker 120*387f9dfdSAndroid Build Coastguard Worker @skipUnless(kernel_version_ge(5,8), "requires kernel >= 5.8") 121*387f9dfdSAndroid Build Coastguard Worker def test_ringbuf_discard(self): 122*387f9dfdSAndroid Build Coastguard Worker self.counter = 0 123*387f9dfdSAndroid Build Coastguard Worker 124*387f9dfdSAndroid Build Coastguard Worker class Data(ct.Structure): 125*387f9dfdSAndroid Build Coastguard Worker _fields_ = [("ts", ct.c_ulonglong)] 126*387f9dfdSAndroid Build Coastguard Worker 127*387f9dfdSAndroid Build Coastguard Worker def cb(ctx, data, size): 128*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(size, ct.sizeof(Data)) 129*387f9dfdSAndroid Build Coastguard Worker event = ct.cast(data, ct.POINTER(Data)).contents 130*387f9dfdSAndroid Build Coastguard Worker self.counter += 1 131*387f9dfdSAndroid Build Coastguard Worker 132*387f9dfdSAndroid Build Coastguard Worker text = b""" 133*387f9dfdSAndroid Build Coastguard WorkerBPF_RINGBUF_OUTPUT(events, 8); 134*387f9dfdSAndroid Build Coastguard Workerstruct data_t { 135*387f9dfdSAndroid Build Coastguard Worker u64 ts; 136*387f9dfdSAndroid Build Coastguard Worker}; 137*387f9dfdSAndroid Build Coastguard Workerint do_sys_nanosleep(void *ctx) { 138*387f9dfdSAndroid Build Coastguard Worker struct data_t *data = events.ringbuf_reserve(sizeof(struct data_t)); 139*387f9dfdSAndroid Build Coastguard Worker if (!data) 140*387f9dfdSAndroid Build Coastguard Worker return 1; 141*387f9dfdSAndroid Build Coastguard Worker data->ts = bpf_ktime_get_ns(); 142*387f9dfdSAndroid Build Coastguard Worker events.ringbuf_discard(data, 0); 143*387f9dfdSAndroid Build Coastguard Worker return 0; 144*387f9dfdSAndroid Build Coastguard Worker} 145*387f9dfdSAndroid Build Coastguard Worker""" 146*387f9dfdSAndroid Build Coastguard Worker b = BPF(text=text) 147*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"nanosleep"), 148*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 149*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"clock_nanosleep"), 150*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 151*387f9dfdSAndroid Build Coastguard Worker b[b"events"].open_ring_buffer(cb) 152*387f9dfdSAndroid Build Coastguard Worker subprocess.call(['sleep', '0.1']) 153*387f9dfdSAndroid Build Coastguard Worker b.ring_buffer_poll() 154*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(self.counter, 0) 155*387f9dfdSAndroid Build Coastguard Worker b.cleanup() 156*387f9dfdSAndroid Build Coastguard Worker 157*387f9dfdSAndroid Build Coastguard Worker @skipUnless(kernel_version_ge(5,8), "requires kernel >= 5.8") 158*387f9dfdSAndroid Build Coastguard Worker def test_ringbuf_query(self): 159*387f9dfdSAndroid Build Coastguard Worker PAGE_SIZE = 8 160*387f9dfdSAndroid Build Coastguard Worker self.counter = 0 161*387f9dfdSAndroid Build Coastguard Worker self.page_counts = 0 162*387f9dfdSAndroid Build Coastguard Worker 163*387f9dfdSAndroid Build Coastguard Worker class Data(ct.Structure): 164*387f9dfdSAndroid Build Coastguard Worker _fields_ = [("page_cnt", ct.c_ulonglong)] 165*387f9dfdSAndroid Build Coastguard Worker 166*387f9dfdSAndroid Build Coastguard Worker def cb(ctx, data, size): 167*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(size, ct.sizeof(Data)) 168*387f9dfdSAndroid Build Coastguard Worker event = ct.cast(data, ct.POINTER(Data)).contents 169*387f9dfdSAndroid Build Coastguard Worker self.page_counts += event.page_cnt 170*387f9dfdSAndroid Build Coastguard Worker self.counter += 1 171*387f9dfdSAndroid Build Coastguard Worker 172*387f9dfdSAndroid Build Coastguard Worker text = b""" 173*387f9dfdSAndroid Build Coastguard WorkerBPF_RINGBUF_OUTPUT(events, %i); 174*387f9dfdSAndroid Build Coastguard Workerstruct data_t { 175*387f9dfdSAndroid Build Coastguard Worker u64 page_cnt; 176*387f9dfdSAndroid Build Coastguard Worker}; 177*387f9dfdSAndroid Build Coastguard Workerint do_sys_nanosleep(void *ctx) { 178*387f9dfdSAndroid Build Coastguard Worker u64 res = 0; 179*387f9dfdSAndroid Build Coastguard Worker res = events.ringbuf_query(BPF_RB_RING_SIZE); 180*387f9dfdSAndroid Build Coastguard Worker if(res == 0) { 181*387f9dfdSAndroid Build Coastguard Worker return 1; 182*387f9dfdSAndroid Build Coastguard Worker } 183*387f9dfdSAndroid Build Coastguard Worker struct data_t data = {res / PAGE_SIZE}; 184*387f9dfdSAndroid Build Coastguard Worker events.ringbuf_output(&data, sizeof(data), 0); 185*387f9dfdSAndroid Build Coastguard Worker return 0; 186*387f9dfdSAndroid Build Coastguard Worker} 187*387f9dfdSAndroid Build Coastguard Worker""" 188*387f9dfdSAndroid Build Coastguard Worker text = text % PAGE_SIZE 189*387f9dfdSAndroid Build Coastguard Worker b = BPF(text=text) 190*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"nanosleep"), 191*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 192*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=b.get_syscall_fnname(b"clock_nanosleep"), 193*387f9dfdSAndroid Build Coastguard Worker fn_name=b"do_sys_nanosleep") 194*387f9dfdSAndroid Build Coastguard Worker b[b"events"].open_ring_buffer(cb) 195*387f9dfdSAndroid Build Coastguard Worker subprocess.call(['sleep', '0.1']) 196*387f9dfdSAndroid Build Coastguard Worker b.ring_buffer_poll() 197*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(self.page_counts / self.counter, PAGE_SIZE) 198*387f9dfdSAndroid Build Coastguard Worker b.cleanup() 199*387f9dfdSAndroid Build Coastguard Worker 200*387f9dfdSAndroid Build Coastguard Workerif __name__ == "__main__": 201*387f9dfdSAndroid Build Coastguard Worker main() 202