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: use_system_cxx_lib && 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 <string.h>
19 
20 void backtrace(int lower_bound) {
21   unw_context_t context;
22   unw_getcontext(&context);
23 
24   unw_cursor_t cursor;
25   unw_init_local(&cursor, &context);
26 
27   int n = 0;
28   do {
29     ++n;
30     if (n > 100) {
31       abort();
32     }
33   } while (unw_step(&cursor) > 0);
34 
35   if (n < lower_bound) {
36     abort();
37   }
38 }
39 
40 void test1(int i) {
41   backtrace(i);
42 }
43 
44 void test2(int i, int j) {
45   backtrace(i);
46   test1(j);
47 }
48 
49 void test3(int i, int j, int k) {
50   backtrace(i);
51   test2(j, k);
52 }
53 
54 void test_no_info() {
55   unw_context_t context;
56   unw_getcontext(&context);
57 
58   unw_cursor_t cursor;
59   unw_init_local(&cursor, &context);
60 
61   unw_proc_info_t info;
62   int ret = unw_get_proc_info(&cursor, &info);
63   if (ret != UNW_ESUCCESS)
64     abort();
65 
66   // Set the IP to an address clearly outside any function.
67   unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)0);
68 
69   ret = unw_get_proc_info(&cursor, &info);
70   if (ret != UNW_ENOINFO)
71     abort();
72 }
73 
74 void test_reg_names() {
75   unw_context_t context;
76   unw_getcontext(&context);
77 
78   unw_cursor_t cursor;
79   unw_init_local(&cursor, &context);
80 
81   int max_reg_num = -100;
82 #if defined(__i386__)
83   max_reg_num = 7;
84 #elif defined(__x86_64__)
85   max_reg_num = 32;
86 #endif
87 
88   const char prefix[] = "unknown";
89   for (int i = -2; i < max_reg_num; ++i) {
90     if (strncmp(prefix, unw_regname(&cursor, i), sizeof(prefix) - 1) == 0)
91       abort();
92   }
93 
94   if (strncmp(prefix, unw_regname(&cursor, max_reg_num + 1),
95               sizeof(prefix) - 1) != 0)
96     abort();
97 }
98 
99 #if defined(__x86_64__)
100 void test_reg_get_set() {
101   unw_context_t context;
102   unw_getcontext(&context);
103 
104   unw_cursor_t cursor;
105   unw_init_local(&cursor, &context);
106 
107   for (int i = 0; i < 17; ++i) {
108     const unw_word_t set_value = 7;
109     if (unw_set_reg(&cursor, i, set_value) != UNW_ESUCCESS)
110       abort();
111 
112     unw_word_t get_value = 0;
113     if (unw_get_reg(&cursor, i, &get_value) != UNW_ESUCCESS)
114       abort();
115 
116     if (set_value != get_value)
117       abort();
118   }
119 }
120 
121 void test_fpreg_get_set() {
122   unw_context_t context;
123   unw_getcontext(&context);
124 
125   unw_cursor_t cursor;
126   unw_init_local(&cursor, &context);
127 
128   // get/set is not implemented for x86_64 fpregs.
129   for (int i = 17; i < 33; ++i) {
130     const unw_fpreg_t set_value = 7;
131     if (unw_set_fpreg(&cursor, i, set_value) != UNW_EBADREG)
132       abort();
133 
134     unw_fpreg_t get_value = 0;
135     if (unw_get_fpreg(&cursor, i, &get_value) != UNW_EBADREG)
136       abort();
137   }
138 }
139 #else
140 void test_reg_get_set() {}
141 void test_fpreg_get_set() {}
142 #endif
143 
144 int main(int, char**) {
145   test1(1);
146   test2(1, 2);
147   test3(1, 2, 3);
148   test_no_info();
149   test_reg_names();
150   test_reg_get_set();
151   test_fpreg_get_set();
152   return 0;
153 }
154