]> git.saurik.com Git - apple/xnu.git/blame_incremental - tools/tests/darwintests/backtracing.c
xnu-3789.41.3.tar.gz
[apple/xnu.git] / tools / tests / darwintests / backtracing.c
... / ...
CommitLineData
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
12static 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
20static void
21expect_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
50static void __attribute__((noinline,not_tail_called))
51expect_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
115static int __attribute__((noinline,not_tail_called))
116recurse_a(unsigned int frames);
117static int __attribute__((noinline,not_tail_called))
118recurse_b(unsigned int frames);
119
120static int __attribute__((noinline,not_tail_called))
121recurse_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
132static int __attribute__((noinline,not_tail_called))
133recurse_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
144static void *
145backtrace_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
163T_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}