]> git.saurik.com Git - apple/libc.git/blob - tests/backtrace.c
Libc-1353.11.2.tar.gz
[apple/libc.git] / tests / backtrace.c
1 #include <darwintest.h>
2 #include <dlfcn.h>
3 #include <execinfo.h>
4 #include <mach-o/dyld_priv.h>
5 #include <stdbool.h>
6 #include <stdlib.h>
7 #include <uuid/uuid.h>
8
9
10 #define MAX_FRAMES 32
11 static const int expected_nframes = 20;
12 static const int skip_nframes = 5;
13
14 static void *observed_bt[MAX_FRAMES] = {};
15 static int observed_nframes = 0;
16 static unsigned int save_fp_at_nframes = 0;
17 static void *save_fp = NULL;
18
19 static int __attribute__((noinline,not_tail_called,disable_tail_calls))
20 recurse_a(unsigned int frames);
21 static int __attribute__((noinline,not_tail_called,disable_tail_calls))
22 recurse_b(unsigned int frames);
23
24 static int __attribute__((noinline,not_tail_called,disable_tail_calls))
25 recurse_a(unsigned int frames)
26 {
27 if (frames == 0) {
28 if (save_fp_at_nframes > 0) {
29 observed_nframes = backtrace_from_fp(save_fp, observed_bt,
30 MAX_FRAMES);
31 } else {
32 observed_nframes = backtrace(observed_bt, MAX_FRAMES);
33 }
34 return 0;
35 } else if (frames == save_fp_at_nframes) {
36 save_fp = __builtin_frame_address(0);
37 }
38
39 return recurse_b(frames - 1);
40 }
41
42 static int __attribute__((noinline,not_tail_called,disable_tail_calls))
43 recurse_b(unsigned int frames)
44 {
45 if (frames == 0) {
46 if (save_fp_at_nframes > 0) {
47 observed_nframes = backtrace_from_fp(save_fp, observed_bt,
48 MAX_FRAMES);
49 } else {
50 observed_nframes = backtrace(observed_bt, MAX_FRAMES);
51 }
52 return 0;
53 } else if (frames == save_fp_at_nframes) {
54 save_fp = __builtin_frame_address(0);
55 }
56
57 return recurse_a(frames - 1);
58 }
59
60 static void __attribute__((noinline,not_tail_called,disable_tail_calls))
61 setup_and_backtrace(unsigned int nframes, unsigned int skip)
62 {
63 save_fp_at_nframes = skip ? skip - 1 : 0;
64 recurse_a(nframes - 1);
65 }
66
67 static bool
68 check_for_setup(int i, struct dl_info *info)
69 {
70 int ret = dladdr(observed_bt[i], info);
71 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "dladdr(%p)", observed_bt[i]);
72 void *setup_fp = (void *)&setup_and_backtrace;
73 return info->dli_saddr == setup_fp;
74 }
75
76 static void __attribute__((noinline,not_tail_called))
77 expect_backtrace(void)
78 {
79 void *recurse_a_fp = (void *)&recurse_a;
80 void *recurse_b_fp = (void *)&recurse_b;
81
82 void *tmp_backtrace[MAX_FRAMES];
83 const int observed_existing_nframes = backtrace(tmp_backtrace, MAX_FRAMES);
84
85 T_EXPECT_EQ(expected_nframes,
86 observed_nframes - observed_existing_nframes,
87 "number of frames traced matches %d", expected_nframes);
88 bool expect_a = !(skip_nframes % 2);
89 bool found_setup = false;
90
91 for (int i = 0; i < observed_nframes; i++) {
92 struct dl_info info;
93 if (check_for_setup(i, &info)) {
94 found_setup = true;
95 break;
96 }
97
98 void *expected_saddr = expect_a ? recurse_a_fp : recurse_b_fp;
99 void *observed_saddr = info.dli_saddr;
100 T_EXPECT_EQ(observed_saddr, expected_saddr,
101 "frame %d (%p: %s) matches", i, observed_bt[i], info.dli_sname);
102 expect_a = !expect_a;
103 }
104
105 T_EXPECT_TRUE(found_setup, "should have found the setup frame");
106 }
107
108 T_DECL(backtrace, "ensure backtrace(3) gives the correct backtrace")
109 {
110 setup_and_backtrace(expected_nframes, 0);
111 expect_backtrace();
112 }
113
114 T_DECL(backtrace_from_fp,
115 "ensure backtrace_from_fp(3) starts from the correct frame")
116 {
117 setup_and_backtrace(expected_nframes + skip_nframes, skip_nframes);
118 expect_backtrace();
119 }
120
121 T_DECL(backtrace_image_offsets,
122 "ensure backtrace_image_offsets(3) provides valid UUIDs and offsets")
123 {
124 setup_and_backtrace(expected_nframes, 0);
125 struct image_offset imgoffs[observed_nframes];
126 backtrace_image_offsets(observed_bt, imgoffs, observed_nframes);
127
128 bool found_setup = false;
129
130 for (int i = 0; i < observed_nframes; i++) {
131 struct dl_info info;
132 if (check_for_setup(i, &info)) {
133 found_setup = true;
134 break;
135 }
136
137 const struct mach_header *mh =
138 dyld_image_header_containing_address(observed_bt[i]);
139
140 uuid_t expected_uuid;
141 bool got_uuid = _dyld_get_image_uuid(mh, expected_uuid);
142 T_QUIET; T_ASSERT_TRUE(got_uuid, "got UUID for Mach-O header");
143
144 T_EXPECT_EQ(uuid_compare(expected_uuid, imgoffs[i].uuid), 0,
145 "frame %d's UUID matches", i);
146 T_EXPECT_EQ((uintptr_t)observed_bt[i] - (uintptr_t)info.dli_fbase,
147 (uintptr_t)imgoffs[i].offset, "frame %d's offset matches", i);
148 }
149
150 T_EXPECT_TRUE(found_setup, "should have found the setup frame");
151 }
152
153 T_DECL(backtrace_symbols, "tests backtrace_symbols")
154 {
155 setup_and_backtrace(expected_nframes, 0);
156
157 char **symbols = backtrace_symbols(observed_bt, observed_nframes);
158
159 bool found_setup = false;
160
161 for (int i = 0; i < observed_nframes; i++) {
162 T_LOG("frame[%d]: %s", i, symbols[i]);
163 if (strstr(symbols[i], "setup_and_backtrace") != NULL) {
164 found_setup = true;
165 }
166 }
167
168 T_EXPECT_TRUE(found_setup, "should have found the setup frame");
169
170 free(symbols);
171 }