]>
Commit | Line | Data |
---|---|---|
70ad1dc8 A |
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 | ||
13 | static void *observed_bt[MAX_FRAMES] = {}; | |
14 | static int observed_nframes = 0; | |
15 | static unsigned int save_fp_at_nframes = 0; | |
16 | static void *save_fp = NULL; | |
17 | ||
18 | static int __attribute__((noinline,not_tail_called,disable_tail_calls)) | |
19 | recurse_a(unsigned int frames); | |
20 | static int __attribute__((noinline,not_tail_called,disable_tail_calls)) | |
21 | recurse_b(unsigned int frames); | |
22 | ||
23 | static int __attribute__((noinline,not_tail_called,disable_tail_calls)) | |
24 | recurse_a(unsigned int frames) | |
25 | { | |
26 | if (frames == 1) { | |
27 | if (save_fp_at_nframes > 0) { | |
28 | observed_nframes = backtrace_from_fp(save_fp, observed_bt, | |
29 | MAX_FRAMES); | |
30 | } else { | |
31 | observed_nframes = backtrace(observed_bt, MAX_FRAMES); | |
32 | } | |
33 | return 0; | |
34 | } else if (frames == save_fp_at_nframes) { | |
35 | save_fp = __builtin_frame_address(0); | |
36 | } | |
37 | ||
38 | return recurse_b(frames - 1); | |
39 | } | |
40 | ||
41 | static int __attribute__((noinline,not_tail_called,disable_tail_calls)) | |
42 | recurse_b(unsigned int frames) | |
43 | { | |
44 | if (frames == 1) { | |
45 | if (save_fp_at_nframes > 0) { | |
46 | observed_nframes = backtrace_from_fp(save_fp, observed_bt, | |
47 | MAX_FRAMES); | |
48 | } else { | |
49 | observed_nframes = backtrace(observed_bt, MAX_FRAMES); | |
50 | } | |
51 | return 0; | |
52 | } else if (frames == save_fp_at_nframes) { | |
53 | save_fp = __builtin_frame_address(0); | |
54 | } | |
55 | ||
56 | return recurse_a(frames - 1); | |
57 | } | |
58 | ||
59 | static void __attribute__((noinline,not_tail_called,disable_tail_calls)) | |
60 | setup_and_backtrace(unsigned int nframes, unsigned int skip_nframes) | |
61 | { | |
62 | save_fp_at_nframes = skip_nframes ? skip_nframes - 1 : 0; | |
63 | recurse_a(nframes - 1); | |
64 | } | |
65 | ||
66 | static bool | |
67 | check_for_setup(int i, struct dl_info *info) | |
68 | { | |
69 | int ret = dladdr(observed_bt[i], info); | |
70 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "dladdr(%p)", observed_bt[i]); | |
71 | void *setup_fp = (void *)&setup_and_backtrace; | |
72 | return info->dli_saddr == setup_fp; | |
73 | } | |
74 | ||
75 | static void __attribute__((noinline)) | |
76 | expect_backtrace(void) | |
77 | { | |
78 | void *recurse_a_fp = (void *)&recurse_a; | |
79 | void *recurse_b_fp = (void *)&recurse_b; | |
80 | ||
81 | void *tmp_backtrace[MAX_FRAMES]; | |
82 | const int observed_existing_nframes = backtrace(tmp_backtrace, MAX_FRAMES); | |
83 | ||
84 | T_EXPECT_EQ(expected_nframes, | |
85 | observed_nframes - observed_existing_nframes, | |
86 | "number of frames traced matches"); | |
87 | bool expect_a = true; | |
88 | bool found_setup = false; | |
89 | ||
90 | for (int i = 0; i < observed_nframes; i++) { | |
91 | struct dl_info info; | |
92 | if (check_for_setup(i, &info)) { | |
93 | found_setup = true; | |
94 | break; | |
95 | } | |
96 | ||
97 | void *expected_saddr = expect_a ? recurse_a_fp : recurse_b_fp; | |
98 | void *observed_saddr = info.dli_saddr; | |
99 | T_EXPECT_GE(observed_saddr, expected_saddr, | |
100 | "frame %d (%p) matches", i, observed_bt[i]); | |
101 | expect_a = !expect_a; | |
102 | } | |
103 | ||
104 | T_EXPECT_TRUE(found_setup, "should have found the setup frame"); | |
105 | } | |
106 | ||
107 | T_DECL(backtrace, "ensure backtrace(3) gives the correct backtrace") | |
108 | { | |
109 | setup_and_backtrace(expected_nframes, 0); | |
110 | expect_backtrace(); | |
111 | } | |
112 | ||
113 | T_DECL(backtrace_from_fp, | |
114 | "ensure backtrace_from_fp(3) starts from the correct frame") | |
115 | { | |
116 | const int skip_nframes = 5; | |
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 | } |