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: Figure out why this fails with Memory Sanitizer.
11// XFAIL: msan
12
13// This test fails on older llvm, when built with picolibc.
14// XFAIL: clang-16 && LIBCXX-PICOLIBC-FIXME
15
16#undef NDEBUG
17#include <assert.h>
18#include <stdlib.h>
19#include <unwind.h>
20
21#define EXPECTED_NUM_FRAMES 50
22#define NUM_FRAMES_UPPER_BOUND 100
23
24__attribute__((noinline)) _Unwind_Reason_Code callback(_Unwind_Context *context,
25 void *cnt) {
26 (void)context;
27 int *i = (int *)cnt;
28 ++*i;
29 if (*i > NUM_FRAMES_UPPER_BOUND) {
30 abort();
31 }
32 return _URC_NO_REASON;
33}
34
35__attribute__((noinline)) void test_backtrace() {
36 int n = 0;
37 _Unwind_Backtrace(&callback, &n);
38 if (n < EXPECTED_NUM_FRAMES) {
39 abort();
40 }
41}
42
43// These functions are effectively the same, but we have to be careful to avoid
44// unwanted optimizations that would mess with the number of frames we expect.
45// Surprisingly, slapping `noinline` is not sufficient -- we also have to avoid
46// writing the function in a way that the compiler can easily spot tail
47// recursion.
48__attribute__((noinline)) int test1(int i);
49__attribute__((noinline)) int test2(int i);
50
51__attribute__((noinline)) int test1(int i) {
52 if (i == 0) {
53 test_backtrace();
54 return 0;
55 } else {
56 return i + test2(i: i - 1);
57 }
58}
59
60__attribute__((noinline)) int test2(int i) {
61 if (i == 0) {
62 test_backtrace();
63 return 0;
64 } else {
65 return i + test1(i: i - 1);
66 }
67}
68
69int main(int, char**) {
70 int total = test1(i: 50);
71 assert(total == 1275);
72 return 0;
73}
74

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