1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9
10 // TODO: Investigate this failure on x86_64 macOS back deployment
11 // XFAIL: stdlib=system && target=x86_64-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}}
12
13 // TODO: Figure out why this fails with Memory Sanitizer.
14 // XFAIL: msan
15
16 #include <libunwind.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20
backtrace(int lower_bound)21 void backtrace(int lower_bound) {
22 unw_context_t context;
23 unw_getcontext(&context);
24
25 unw_cursor_t cursor;
26 unw_init_local(&cursor, &context);
27
28 char buffer[1024];
29 unw_word_t offset = 0;
30
31 int n = 0;
32 do {
33 n++;
34 if (unw_get_proc_name(&cursor, buffer, sizeof(buffer), &offset) == 0) {
35 fprintf(stderr, "Frame %d: %s+%p\n", n, buffer, (void*)offset);
36 } else {
37 fprintf(stderr, "Frame %d: Could not get name for cursor\n", n);
38 }
39 if (n > 100) {
40 abort();
41 }
42 } while (unw_step(&cursor) > 0);
43
44 if (n < lower_bound) {
45 abort();
46 }
47 }
48
test1(int i)49 __attribute__((noinline)) void test1(int i) {
50 fprintf(stderr, "starting %s\n", __func__);
51 backtrace(i);
52 fprintf(stderr, "finished %s\n", __func__); // ensure return address is saved
53 }
54
test2(int i,int j)55 __attribute__((noinline)) void test2(int i, int j) {
56 fprintf(stderr, "starting %s\n", __func__);
57 backtrace(i);
58 test1(j);
59 fprintf(stderr, "finished %s\n", __func__); // ensure return address is saved
60 }
61
test3(int i,int j,int k)62 __attribute__((noinline)) void test3(int i, int j, int k) {
63 fprintf(stderr, "starting %s\n", __func__);
64 backtrace(i);
65 test2(j, k);
66 fprintf(stderr, "finished %s\n", __func__); // ensure return address is saved
67 }
68
test_no_info()69 void test_no_info() {
70 unw_context_t context;
71 unw_getcontext(&context);
72
73 unw_cursor_t cursor;
74 unw_init_local(&cursor, &context);
75
76 unw_proc_info_t info;
77 int ret = unw_get_proc_info(&cursor, &info);
78 if (ret != UNW_ESUCCESS)
79 abort();
80
81 // Set the IP to an address clearly outside any function.
82 unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)0);
83
84 ret = unw_get_proc_info(&cursor, &info);
85 if (ret != UNW_ENOINFO)
86 abort();
87 }
88
test_reg_names()89 void test_reg_names() {
90 unw_context_t context;
91 unw_getcontext(&context);
92
93 unw_cursor_t cursor;
94 unw_init_local(&cursor, &context);
95
96 int max_reg_num = -100;
97 #if defined(__i386__)
98 max_reg_num = 7;
99 #elif defined(__x86_64__)
100 max_reg_num = 32;
101 #endif
102
103 const char prefix[] = "unknown";
104 for (int i = -2; i < max_reg_num; ++i) {
105 if (strncmp(prefix, unw_regname(&cursor, i), sizeof(prefix) - 1) == 0)
106 abort();
107 }
108
109 if (strncmp(prefix, unw_regname(&cursor, max_reg_num + 1),
110 sizeof(prefix) - 1) != 0)
111 abort();
112 }
113
114 #if defined(__x86_64__)
test_reg_get_set()115 void test_reg_get_set() {
116 unw_context_t context;
117 unw_getcontext(&context);
118
119 unw_cursor_t cursor;
120 unw_init_local(&cursor, &context);
121
122 for (int i = 0; i < 17; ++i) {
123 const unw_word_t set_value = 7;
124 if (unw_set_reg(&cursor, i, set_value) != UNW_ESUCCESS)
125 abort();
126
127 unw_word_t get_value = 0;
128 if (unw_get_reg(&cursor, i, &get_value) != UNW_ESUCCESS)
129 abort();
130
131 if (set_value != get_value)
132 abort();
133 }
134 }
135
test_fpreg_get_set()136 void test_fpreg_get_set() {
137 unw_context_t context;
138 unw_getcontext(&context);
139
140 unw_cursor_t cursor;
141 unw_init_local(&cursor, &context);
142
143 // get/set is not implemented for x86_64 fpregs.
144 for (int i = 17; i < 33; ++i) {
145 const unw_fpreg_t set_value = 7;
146 if (unw_set_fpreg(&cursor, i, set_value) != UNW_EBADREG)
147 abort();
148
149 unw_fpreg_t get_value = 0;
150 if (unw_get_fpreg(&cursor, i, &get_value) != UNW_EBADREG)
151 abort();
152 }
153 }
154 #else
test_reg_get_set()155 void test_reg_get_set() {}
test_fpreg_get_set()156 void test_fpreg_get_set() {}
157 #endif
158
main(int,char **)159 int main(int, char**) {
160 test1(3);
161 test2(3, 4);
162 test3(3, 4, 5);
163 test_no_info();
164 test_reg_names();
165 test_reg_get_set();
166 test_fpreg_get_set();
167 return 0;
168 }
169