-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsgxpagefaulttrace_bpf.py
executable file
·128 lines (104 loc) · 3.56 KB
/
sgxpagefaulttrace_bpf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/python
from bcc import BPF
from ctypes import c_int
from time import sleep, strftime
import argparse
SGX_FAULT_BPF = """
#include <uapi/linux/ptrace.h>
enum stat_types {
S_FAULTS = 1,
S_FIRST_PAGE = 2,
S_PAGES = 3,
S_UNACCOUNTED = 4,
S_MAXSTAT
};
BPF_ARRAY(stats, u64, S_MAXSTAT);
static void stats_increment(int key) {
u64 *leaf = stats.lookup(&key);
if (leaf) (*leaf)++;
}
/* Array sizes for max supported enclave size:
* 128GB: 524288
* 64GB: 262144
* 32GB: 131072
* 16GB: 65536
* 8GB: 32768
* Example:
* For bitmap to be big enough for enclaves up to 128 GB: 524288 * 64 * 4096
*
* Large arrays take longer to clear and therefore might only work accurately
* with larger intervals, so support 8GB for now.
*/
BPF_ARRAY(pages_seen, u64, 32768);
/* Returns -1 if address could not be looked up. Otherwise returns 1 if page has
* been seen before, or 0 if not.
*/
static int page_seen_test_and_set(struct pt_regs *ctx, u64 addr) {
u64 addr_idx = addr / 4096;
int stat = S_FIRST_PAGE;
u64 *first_page_idx = stats.lookup(&stat);
if (!first_page_idx) return -1;
if (!*first_page_idx)
(*first_page_idx) = addr_idx;
// We don't know the enclave range, so use first observed address as pivot.
// First bit in bitmap represents first observed page.
u64 idx;
if (addr_idx >= *first_page_idx)
idx = addr_idx - *first_page_idx;
else
// Wrap around, pages lower than the first page are at end of bitmap
idx = 32768 * 64 - (*first_page_idx - addr_idx);
// This can happen if the enclave is larger than what the pages_seen array
// can hold, or if there are multiple enclaves used at once.
if (idx > 32768 * 64) return -1;
// Every u64 in the array represents 64 pages
int idx_blk = (idx / 64);
u64 *page_blk = pages_seen.lookup(&idx_blk);
if (!page_blk) return -1;
int res = ((*page_blk) & ((u64) 1 << (idx % 64))) ? 1 : 0;
(*page_blk) |= (u64) 1 << (idx % 64);
return res;
}
int sgx_fault_page_probe0(struct pt_regs *ctx, void *vma, unsigned long addr, unsigned int flags)
{
stats_increment(S_FAULTS);
int res = page_seen_test_and_set(ctx, addr);
if (res == -1)
stats_increment(S_UNACCOUNTED);
else if (res == 0)
stats_increment(S_PAGES);
return 0;
}
"""
# Stat indexes
S_FAULTS = c_int(1)
S_FIRST_PAGE = c_int(2)
S_PAGES = c_int(3)
S_UNACCOUNTED= c_int(4)
parser = argparse.ArgumentParser(description="Trace SGX page faults.",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("-i", "--interval", default=1, type=int,
help="measurement and output interval, in seconds")
parser.add_argument("-c", "--cumulative", action="store_true",
help="do not clear counts at each interval")
args = parser.parse_args()
# Load BPF program
b = BPF(text=SGX_FAULT_BPF)
b.attach_kprobe(event="sgx_fault_page", fn_name="sgx_fault_page_probe0")
print("Tracing... Ctrl-C to end.")
# output
print("[%s] %-12s %-12s %-12s %-12s %-12s" % (strftime("%H:%M:%S"), "PAGES", "SIZE (MB)", "UNACCOUNTED", "FAULTS", "BANDWIDTH (MB)"))
while (1):
try:
sleep(args.interval)
except KeyboardInterrupt:
exit()
pages = b["stats"][S_PAGES].value
pgsize = pages/256.0
faults = b["stats"][S_FAULTS].value
bandwidth = faults/256.0
unaccounted = b["stats"][S_UNACCOUNTED].value
print("[%s] %-12s %-12.2f %-12s %-12s %-12.2f" % (strftime("%H:%M:%S"), str(pages), pgsize, unaccounted, str(faults), bandwidth))
if not args.cumulative:
b["stats"].clear()
b["pages_seen"].clear()