]>
Commit | Line | Data |
---|---|---|
1 | #include <CoreSymbolication/CoreSymbolication.h> | |
2 | #include <darwintest.h> | |
3 | #include <dispatch/dispatch.h> | |
4 | #include <execinfo.h> | |
5 | #include <pthread.h> | |
6 | #include <sys/sysctl.h> | |
7 | ||
8 | #define USER_FRAMES (12) | |
9 | ||
10 | #define NON_RECURSE_FRAMES (5) | |
11 | ||
12 | static const char *user_bt[USER_FRAMES] = { | |
13 | NULL, NULL, | |
14 | "backtrace_thread", | |
15 | "recurse_a", "recurse_b", "recurse_a", "recurse_b", | |
16 | "recurse_a", "recurse_b", "recurse_a", | |
17 | "expect_stack", NULL | |
18 | }; | |
19 | ||
20 | static void | |
21 | expect_frame(const char **bt, unsigned int bt_len, CSSymbolRef symbol, | |
22 | unsigned long addr, unsigned int bt_idx, unsigned int max_frames) | |
23 | { | |
24 | const char *name; | |
25 | unsigned int frame_idx = max_frames - bt_idx - 1; | |
26 | ||
27 | if (bt[frame_idx] == NULL) { | |
28 | T_LOG("frame %2u: skipping system frame", frame_idx); | |
29 | return; | |
30 | } | |
31 | ||
32 | if (CSIsNull(symbol)) { | |
33 | T_FAIL("invalid symbol for address %#lx at frame %d", addr, frame_idx); | |
34 | return; | |
35 | } | |
36 | ||
37 | if (frame_idx >= bt_len) { | |
38 | T_FAIL("unexpected frame '%s' (%#lx) at index %u", | |
39 | CSSymbolGetName(symbol), addr, frame_idx); | |
40 | return; | |
41 | } | |
42 | ||
43 | name = CSSymbolGetName(symbol); | |
44 | T_QUIET; T_ASSERT_NOTNULL(name, NULL); | |
45 | T_EXPECT_EQ_STR(name, bt[frame_idx], | |
46 | "frame %2u: saw '%s', expected '%s'", | |
47 | frame_idx, name, bt[frame_idx]); | |
48 | } | |
49 | ||
50 | static void __attribute__((noinline,not_tail_called)) | |
51 | expect_stack(void) | |
52 | { | |
53 | uint64_t bt[USER_FRAMES] = { 0 }; | |
54 | unsigned int bt_len = USER_FRAMES; | |
55 | int err; | |
56 | size_t bt_filled; | |
57 | ||
58 | static dispatch_once_t expect_stacks_once; | |
59 | static bool k64; | |
60 | static CSSymbolicatorRef user_symb; | |
61 | ||
62 | dispatch_once(&expect_stacks_once, ^(void) { | |
63 | int errb; | |
64 | int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 /* kernproc */ }; | |
65 | ||
66 | struct kinfo_proc kp; | |
67 | size_t len; | |
68 | ||
69 | len = sizeof(kp); | |
70 | errb = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kp, &len, NULL, 0); | |
71 | T_QUIET; T_ASSERT_POSIX_SUCCESS(errb, | |
72 | "sysctl({ CTL_KERN, KERN_PROC, KERN_PROC_PID, 0})"); | |
73 | ||
74 | k64 = kp.kp_proc.p_flag & P_LP64; | |
75 | T_LOG("executing with a %s-bit kernel", k64 ? "64" : "32"); | |
76 | ||
77 | user_symb = CSSymbolicatorCreateWithTask(mach_task_self()); | |
78 | T_QUIET; T_ASSERT_FALSE(CSIsNull(user_symb), NULL); | |
79 | T_QUIET; T_ASSERT_TRUE(CSSymbolicatorIsTaskValid(user_symb), NULL); | |
80 | }); | |
81 | ||
82 | bt_filled = USER_FRAMES; | |
83 | err = sysctlbyname("kern.backtrace.user", bt, &bt_filled, NULL, 0); | |
84 | if (err == ENOENT) { | |
85 | T_SKIP("release kernel: kern.backtrace.user sysctl returned ENOENT"); | |
86 | } | |
87 | T_ASSERT_POSIX_SUCCESS(err, "sysctlbyname(\"kern.backtrace.user\")"); | |
88 | ||
89 | bt_len = (unsigned int)bt_filled; | |
90 | T_EXPECT_EQ(bt_len, (unsigned int)USER_FRAMES, | |
91 | "%u frames should be present in backtrace", (unsigned int)USER_FRAMES); | |
92 | ||
93 | for (unsigned int i = 0; i < bt_len; i++) { | |
94 | uintptr_t addr; | |
95 | #if !defined(__LP64__) | |
96 | /* | |
97 | * Backtrace frames come out as kernel words; convert them back to user | |
98 | * uintptr_t for 32-bit processes. | |
99 | */ | |
100 | if (k64) { | |
101 | addr = (uintptr_t)(bt[i]); | |
102 | } else { | |
103 | addr = (uintptr_t)(((uint32_t *)bt)[i]); | |
104 | } | |
105 | #else /* defined(__LP32__) */ | |
106 | addr = (uintptr_t)bt[i]; | |
107 | #endif /* defined(__LP32__) */ | |
108 | ||
109 | CSSymbolRef symbol = CSSymbolicatorGetSymbolWithAddressAtTime( | |
110 | user_symb, addr, kCSNow); | |
111 | expect_frame(user_bt, USER_FRAMES, symbol, addr, i, bt_len); | |
112 | } | |
113 | } | |
114 | ||
115 | static int __attribute__((noinline,not_tail_called)) | |
116 | recurse_a(unsigned int frames); | |
117 | static int __attribute__((noinline,not_tail_called)) | |
118 | recurse_b(unsigned int frames); | |
119 | ||
120 | static int __attribute__((noinline,not_tail_called)) | |
121 | recurse_a(unsigned int frames) | |
122 | { | |
123 | if (frames == 1) { | |
124 | expect_stack(); | |
125 | getpid(); | |
126 | return 0; | |
127 | } | |
128 | ||
129 | return recurse_b(frames - 1) + 1; | |
130 | } | |
131 | ||
132 | static int __attribute__((noinline,not_tail_called)) | |
133 | recurse_b(unsigned int frames) | |
134 | { | |
135 | if (frames == 1) { | |
136 | expect_stack(); | |
137 | getpid(); | |
138 | return 0; | |
139 | } | |
140 | ||
141 | return recurse_a(frames - 1) + 1; | |
142 | } | |
143 | ||
144 | static void * | |
145 | backtrace_thread(void *arg) | |
146 | { | |
147 | #pragma unused(arg) | |
148 | unsigned int calls; | |
149 | ||
150 | /* | |
151 | * backtrace_thread, recurse_a, recurse_b, ..., __sysctlbyname | |
152 | * | |
153 | * Always make one less call for this frame (backtrace_thread). | |
154 | */ | |
155 | calls = USER_FRAMES - NON_RECURSE_FRAMES; | |
156 | ||
157 | T_LOG("backtrace thread calling into %d frames (already at %d frames)", | |
158 | calls, NON_RECURSE_FRAMES); | |
159 | (void)recurse_a(calls); | |
160 | return NULL; | |
161 | } | |
162 | ||
163 | T_DECL(backtrace_user, "test that the kernel can backtrace user stacks", | |
164 | T_META_ALL_VALID_ARCHS(YES)) | |
165 | { | |
166 | pthread_t thread; | |
167 | ||
168 | T_QUIET; T_ASSERT_POSIX_ZERO(pthread_create(&thread, NULL, backtrace_thread, | |
169 | NULL), "create additional thread to backtrace"); | |
170 | } |