1local suite = require("test_helper")
2local TestClang = {}
3
4function TestClang:test_probe_read1()
5  local text = [[
6#include <linux/sched.h>
7#include <uapi/linux/ptrace.h>
8int count_sched(struct pt_regs *ctx, struct task_struct *prev) {
9    pid_t p = prev->pid;
10    return (p != -1);
11}
12]]
13  local b = BPF:new{text=text, debug=0}
14  local fn = b:load_func("count_sched", 'BPF_PROG_TYPE_KPROBE')
15end
16
17function TestClang:test_probe_read2()
18  local text = [[
19#include <linux/sched.h>
20#include <uapi/linux/ptrace.h>
21int count_foo(struct pt_regs *ctx, unsigned long a, unsigned long b) {
22    return (a != b);
23}
24]]
25  local b = BPF:new{text=text, debug=0}
26  local fn = b:load_func("count_foo", 'BPF_PROG_TYPE_KPROBE')
27end
28
29function TestClang:test_probe_read_keys()
30  local text = [[
31#include <uapi/linux/ptrace.h>
32#include <linux/blkdev.h>
33BPF_HASH(start, struct request *);
34int do_request(struct pt_regs *ctx, struct request *req) {
35  u64 ts = bpf_ktime_get_ns();
36  start.update(&req, &ts);
37  return 0;
38}
39
40int do_completion(struct pt_regs *ctx, struct request *req) {
41  u64 *tsp = start.lookup(&req);
42  if (tsp != 0) {
43    start.delete(&req);
44  }
45  return 0;
46}
47  ]]
48  local b = BPF:new{text=text, debug=0}
49  local fns = b:load_funcs('BPF_PROG_TYPE_KPROBE')
50end
51
52function TestClang:test_sscanf()
53  local text = [[
54BPF_HASH(stats, int, struct { u64 a; u64 b; u32 c:18; u32 d:14; struct { u32 a; u32 b; } s; }, 10);
55
56int foo(void *ctx) {
57    return 0;
58}
59]]
60  local b = BPF:new{text=text, debug=0}
61  local fn = b:load_func("foo", 'BPF_PROG_TYPE_KPROBE')
62  local t = b:get_table("stats")
63  local s1 = t:key_sprintf(2)
64
65  assert_equals(s1, "0x2")
66
67  local s2 = t:leaf_sprintf({{2, 3, 4, 1, {5, 6}}})
68  local l = t:leaf_scanf(s2)
69
70  assert_equals(tonumber(l.a), 2)
71  assert_equals(tonumber(l.b), 3)
72  assert_equals(tonumber(l.c), 4)
73  assert_equals(tonumber(l.d), 1)
74  assert_equals(tonumber(l.s.a), 5)
75  assert_equals(tonumber(l.s.b), 6)
76end
77
78function TestClang:test_sscanf_array()
79  local text = [[ BPF_HASH(stats, int, struct { u32 a[3]; u32 b; }, 10); ]]
80
81  local b = BPF:new{text=text, debug=0}
82  local t = b:get_table("stats")
83
84  local s1 = t:key_sprintf(2)
85  assert_equals(s1, "0x2")
86
87  local s2 = t:leaf_sprintf({{{1, 2, 3}, 4}})
88  assert_equals(s2, "{ [ 0x1 0x2 0x3 ] 0x4 }")
89
90  local l = t:leaf_scanf(s2)
91  assert_equals(l.a[0], 1)
92  assert_equals(l.a[1], 2)
93  assert_equals(l.a[2], 3)
94  assert_equals(l.b, 4)
95end
96
97function TestClang:test_iosnoop()
98  local text = [[
99#include <linux/blkdev.h>
100#include <uapi/linux/ptrace.h>
101
102struct key_t {
103    struct request *req;
104};
105
106BPF_HASH(start, struct key_t, u64, 1024);
107int do_request(struct pt_regs *ctx, struct request *req) {
108    struct key_t key = {};
109
110    bpf_trace_printk("traced start %d\\n", req->__data_len);
111
112    return 0;
113}
114]]
115
116  local b = BPF:new{text=text, debug=0}
117  local fn = b:load_func("do_request", 'BPF_PROG_TYPE_KPROBE')
118end
119
120function TestClang:test_blk_start_request()
121  local text = [[
122#include <linux/blkdev.h>
123#include <uapi/linux/ptrace.h>
124int do_request(struct pt_regs *ctx, int req) {
125    bpf_trace_printk("req ptr: 0x%x\n", req);
126    return 0;
127}
128]]
129  local b = BPF:new{text=text, debug=0}
130  local fn = b:load_func("do_request", 'BPF_PROG_TYPE_KPROBE')
131end
132
133function TestClang:test_bpf_hash()
134  local text = [[
135BPF_HASH(table1);
136BPF_HASH(table2, u32);
137BPF_HASH(table3, u32, int);
138]]
139  local b = BPF:new{text=text, debug=0}
140end
141
142function TestClang:test_consecutive_probe_read()
143  local text = [[
144#include <linux/fs.h>
145#include <linux/mount.h>
146BPF_HASH(table1, struct super_block *);
147int trace_entry(struct pt_regs *ctx, struct file *file) {
148    if (!file) return 0;
149    struct vfsmount *mnt = file->f_path.mnt;
150    if (mnt) {
151        struct super_block *k = mnt->mnt_sb;
152        u64 zero = 0;
153        table1.update(&k, &zero);
154        k = mnt->mnt_sb;
155        table1.update(&k, &zero);
156    }
157
158    return 0;
159}
160]]
161  local b = BPF:new{text=text, debug=0}
162  local fn = b:load_func("trace_entry", 'BPF_PROG_TYPE_KPROBE')
163end
164
165function TestClang:test_nested_probe_read()
166  local text = [[
167#include <linux/fs.h>
168int trace_entry(struct pt_regs *ctx, struct file *file) {
169    if (!file) return 0;
170    const char *name = file->f_path.dentry->d_name.name;
171    bpf_trace_printk("%s\\n", name);
172    return 0;
173}
174]]
175  local b = BPF:new{text=text, debug=0}
176  local fn = b:load_func("trace_entry", 'BPF_PROG_TYPE_KPROBE')
177end
178
179function TestClang:test_char_array_probe()
180  local b = BPF:new{text=[[#include <linux/blkdev.h>
181int kprobe__blk_update_request(struct pt_regs *ctx, struct request *req) {
182    bpf_trace_printk("%s\\n", req->rq_disk->disk_name);
183    return 0;
184}]]}
185end
186
187function TestClang:test_probe_read_helper()
188  local b = BPF:new{text=[[
189#include <linux/fs.h>
190static void print_file_name(struct file *file) {
191    if (!file) return;
192    const char *name = file->f_path.dentry->d_name.name;
193    bpf_trace_printk("%s\\n", name);
194}
195static void print_file_name2(int unused, struct file *file) {
196    print_file_name(file);
197}
198int trace_entry1(struct pt_regs *ctx, struct file *file) {
199    print_file_name(file);
200    return 0;
201}
202int trace_entry2(struct pt_regs *ctx, int unused, struct file *file) {
203    print_file_name2(unused, file);
204    return 0;
205}
206]]}
207  local fn1 = b:load_func("trace_entry1", 'BPF_PROG_TYPE_KPROBE')
208  local fn2 = b:load_func("trace_entry2", 'BPF_PROG_TYPE_KPROBE')
209end
210
211function TestClang:test_probe_struct_assign()
212  local b = BPF:new{text = [[
213#include <uapi/linux/ptrace.h>
214struct args_t {
215    const char *filename;
216    int flags;
217    int mode;
218};
219int kprobe__sys_open(struct pt_regs *ctx, const char *filename,
220        int flags, int mode) {
221    struct args_t args = {};
222    args.filename = filename;
223    args.flags = flags;
224    args.mode = mode;
225    bpf_trace_printk("%s\\n", args.filename);
226    return 0;
227};
228]]}
229end
230
231function TestClang:test_task_switch()
232  local b = BPF:new{text=[[
233#include <uapi/linux/ptrace.h>
234#include <linux/sched.h>
235struct key_t {
236  u32 prev_pid;
237  u32 curr_pid;
238};
239BPF_HASH(stats, struct key_t, u64, 1024);
240int kprobe__finish_task_switch(struct pt_regs *ctx, struct task_struct *prev) {
241  struct key_t key = {};
242  u64 zero = 0, *val;
243  key.curr_pid = bpf_get_current_pid_tgid();
244  key.prev_pid = prev->pid;
245
246  val = stats.lookup_or_init(&key, &zero);
247  (*val)++;
248  return 0;
249}
250]]}
251end
252
253function TestClang:test_probe_simple_assign()
254  local b = BPF:new{text=[[
255#include <uapi/linux/ptrace.h>
256#include <linux/gfp.h>
257struct leaf { size_t size; };
258BPF_HASH(simple_map, u32, struct leaf);
259int kprobe____kmalloc(struct pt_regs *ctx, size_t size) {
260    u32 pid = bpf_get_current_pid_tgid();
261    struct leaf* leaf = simple_map.lookup(&pid);
262    if (leaf)
263        leaf->size += size;
264    return 0;
265}]]}
266end
267
268function TestClang:test_unop_probe_read()
269  local text = [[
270#include <linux/blkdev.h>
271int trace_entry(struct pt_regs *ctx, struct request *req) {
272    if (!(req->bio->bi_flags & 1))
273        return 1;
274    if (((req->bio->bi_flags)))
275        return 1;
276    return 0;
277}
278]]
279  local b = BPF:new{text=text}
280  local fn = b:load_func("trace_entry", 'BPF_PROG_TYPE_KPROBE')
281end
282
283function TestClang:test_complex_leaf_types()
284  local text = [[
285struct list;
286struct list {
287  struct list *selfp;
288  struct list *another_selfp;
289  struct list *selfp_array[2];
290};
291struct empty {
292};
293union emptyu {
294  struct empty *em1;
295  struct empty em2;
296  struct empty em3;
297  struct empty em4;
298};
299BPF_ARRAY(t1, struct list, 1);
300BPF_ARRAY(t2, struct list *, 1);
301BPF_ARRAY(t3, union emptyu, 1);
302]]
303  local b = BPF:new{text=text}
304  local ffi = require("ffi")
305
306  -- TODO: ptrs?
307  assert_equals(ffi.sizeof(b:get_table("t3").c_leaf), 8)
308end
309
310function TestClang:test_cflags()
311  local text = [[
312#ifndef MYFLAG
313#error "MYFLAG not set as expected"
314#endif
315]]
316  local b = BPF:new{text=text, cflags={"-DMYFLAG"}}
317end
318
319function TestClang:test_exported_maps()
320  local b1 = BPF{text=[[BPF_TABLE_PUBLIC("hash", int, int, table1, 10);]]}
321  local b2 = BPF{text=[[BPF_TABLE("extern", int, int, table1, 10);]]}
322end
323
324function TestClang:test_syntax_error()
325  assert_error_msg_contains(
326    "failed to compile BPF module",
327    BPF.new,
328    BPF, {text=[[int failure(void *ctx) { if (); return 0; }]]})
329end
330
331suite("TestClang", TestClang)
332