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=apple-libc++ && 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
21void 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, format: "Frame %d: %s+%p\n", n, buffer, (void*)offset);
36 } else {
37 fprintf(stderr, format: "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
49__attribute__((noinline)) void test1(int i) {
50 fprintf(stderr, format: "starting %s\n", __func__);
51 backtrace(lower_bound: i);
52 fprintf(stderr, format: "finished %s\n", __func__); // ensure return address is saved
53}
54
55__attribute__((noinline)) void test2(int i, int j) {
56 fprintf(stderr, format: "starting %s\n", __func__);
57 backtrace(lower_bound: i);
58 test1(i: j);
59 fprintf(stderr, format: "finished %s\n", __func__); // ensure return address is saved
60}
61
62__attribute__((noinline)) void test3(int i, int j, int k) {
63 fprintf(stderr, format: "starting %s\n", __func__);
64 backtrace(lower_bound: i);
65 test2(i: j, j: k);
66 fprintf(stderr, format: "finished %s\n", __func__); // ensure return address is saved
67}
68
69void 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
89void 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(s1: prefix, unw_regname(&cursor, i), n: sizeof(prefix) - 1) == 0)
106 abort();
107 }
108
109 if (strncmp(s1: prefix, unw_regname(&cursor, max_reg_num + 1),
110 n: sizeof(prefix) - 1) != 0)
111 abort();
112}
113
114#if defined(__x86_64__)
115void 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
136void 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
155void test_reg_get_set() {}
156void test_fpreg_get_set() {}
157#endif
158
159int main(int, char**) {
160 test1(i: 3);
161 test2(i: 3, j: 4);
162 test3(i: 3, j: 4, k: 5);
163 test_no_info();
164 test_reg_names();
165 test_reg_get_set();
166 test_fpreg_get_set();
167 return 0;
168}
169

source code of libunwind/test/libunwind_01.pass.cpp