blob: 721328430ae01a36580971cf71fc9a7408f5c0ae [file] [log] [blame]
Austin Schuh906616c2019-01-21 20:25:11 -08001// Copyright (c) 2004, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "utilities.h"
31
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070032#include <cstdio>
33#include <cstdlib>
Austin Schuh906616c2019-01-21 20:25:11 -080034#include "config.h"
35#include "base/commandlineflags.h"
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070036#include <glog/logging.h>
Austin Schuh906616c2019-01-21 20:25:11 -080037#include "stacktrace.h"
38
James Kuszmaulba0ac1a2022-08-12 16:29:30 -070039#ifdef HAVE_EXECINFO_BACKTRACE_SYMBOLS
Austin Schuh906616c2019-01-21 20:25:11 -080040# include <execinfo.h>
41#endif
42
43using namespace GOOGLE_NAMESPACE;
44
45#ifdef HAVE_STACKTRACE
46
47// Obtain a backtrace, verify that the expected callers are present in the
48// backtrace, and maybe print the backtrace to stdout.
49
50// The sequence of functions whose return addresses we expect to see in the
51// backtrace.
52const int BACKTRACE_STEPS = 6;
53
54struct AddressRange {
55 const void *start, *end;
56};
57
58// Expected function [start,end] range.
59AddressRange expected_range[BACKTRACE_STEPS];
60
61#if __GNUC__
62// Using GCC extension: address of a label can be taken with '&&label'.
63// Start should be a label somewhere before recursive call, end somewhere
64// after it.
65#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \
66 do { \
67 (prange)->start = &&start_label; \
68 (prange)->end = &&end_label; \
69 CHECK_LT((prange)->start, (prange)->end); \
70 } while (0)
71// This macro expands into "unmovable" code (opaque to GCC), and that
72// prevents GCC from moving a_label up or down in the code.
73// Without it, there is no code following the 'end' label, and GCC
74// (4.3.1, 4.4.0) thinks it safe to assign &&end an address that is before
75// the recursive call.
76#define DECLARE_ADDRESS_LABEL(a_label) \
77 a_label: do { __asm__ __volatile__(""); } while (0)
78// Gcc 4.4.0 may split function into multiple chunks, and the chunk
79// performing recursive call may end up later in the code then the return
80// instruction (this actually happens with FDO).
81// Adjust function range from __builtin_return_address.
82#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) \
83 do { \
84 void *ra = __builtin_return_address(0); \
85 CHECK_LT((prange)->start, ra); \
86 if (ra > (prange)->end) { \
87 printf("Adjusting range from %p..%p to %p..%p\n", \
88 (prange)->start, (prange)->end, \
89 (prange)->start, ra); \
90 (prange)->end = ra; \
91 } \
92 } while (0)
93#else
94// Assume the Check* functions below are not longer than 256 bytes.
95#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \
96 do { \
97 (prange)->start = reinterpret_cast<const void *>(&fn); \
98 (prange)->end = reinterpret_cast<const char *>(&fn) + 256; \
99 } while (0)
100#define DECLARE_ADDRESS_LABEL(a_label) do { } while (0)
101#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) do { } while (0)
102#endif // __GNUC__
103
104//-----------------------------------------------------------------------//
105
106static void CheckRetAddrIsInFunction(void *ret_addr, const AddressRange &range)
107{
108 CHECK_GE(ret_addr, range.start);
109 CHECK_LE(ret_addr, range.end);
110}
111
112//-----------------------------------------------------------------------//
113
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700114#if defined(__clang__)
115#pragma clang diagnostic push
116#pragma clang diagnostic ignored "-Wgnu-label-as-value"
117#endif
118
Austin Schuh906616c2019-01-21 20:25:11 -0800119void ATTRIBUTE_NOINLINE CheckStackTrace(int);
120static void ATTRIBUTE_NOINLINE CheckStackTraceLeaf(void) {
121 const int STACK_LEN = 10;
122 void *stack[STACK_LEN];
123 int size;
124
125 ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]);
126 INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
127 DECLARE_ADDRESS_LABEL(start);
128 size = GetStackTrace(stack, STACK_LEN, 0);
129 printf("Obtained %d stack frames.\n", size);
130 CHECK_GE(size, 1);
131 CHECK_LE(size, STACK_LEN);
132
133 if (1) {
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700134#ifdef HAVE_EXECINFO_BACKTRACE_SYMBOLS
Austin Schuh906616c2019-01-21 20:25:11 -0800135 char **strings = backtrace_symbols(stack, size);
136 printf("Obtained %d stack frames.\n", size);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700137 for (int i = 0; i < size; i++) {
Austin Schuh906616c2019-01-21 20:25:11 -0800138 printf("%s %p\n", strings[i], stack[i]);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700139 }
140
141 union {
142 void (*p1)(int);
143 void* p2;
144 } p = {&CheckStackTrace};
145
146 printf("CheckStackTrace() addr: %p\n", p.p2);
Austin Schuh906616c2019-01-21 20:25:11 -0800147 free(strings);
148#endif
149 }
150 for (int i = 0; i < BACKTRACE_STEPS; i++) {
151 printf("Backtrace %d: expected: %p..%p actual: %p ... ",
152 i, expected_range[i].start, expected_range[i].end, stack[i]);
153 fflush(stdout);
154 CheckRetAddrIsInFunction(stack[i], expected_range[i]);
155 printf("OK\n");
156 }
157 DECLARE_ADDRESS_LABEL(end);
158}
159
160//-----------------------------------------------------------------------//
161
162/* Dummy functions to make the backtrace more interesting. */
163static void ATTRIBUTE_NOINLINE CheckStackTrace4(int i) {
164 ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[2]);
165 INIT_ADDRESS_RANGE(CheckStackTrace4, start, end, &expected_range[1]);
166 DECLARE_ADDRESS_LABEL(start);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700167 for (int j = i; j >= 0; j--) {
Austin Schuh906616c2019-01-21 20:25:11 -0800168 CheckStackTraceLeaf();
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700169 }
Austin Schuh906616c2019-01-21 20:25:11 -0800170 DECLARE_ADDRESS_LABEL(end);
171}
172static void ATTRIBUTE_NOINLINE CheckStackTrace3(int i) {
173 ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[3]);
174 INIT_ADDRESS_RANGE(CheckStackTrace3, start, end, &expected_range[2]);
175 DECLARE_ADDRESS_LABEL(start);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700176 for (int j = i; j >= 0; j--) {
Austin Schuh906616c2019-01-21 20:25:11 -0800177 CheckStackTrace4(j);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700178 }
Austin Schuh906616c2019-01-21 20:25:11 -0800179 DECLARE_ADDRESS_LABEL(end);
180}
181static void ATTRIBUTE_NOINLINE CheckStackTrace2(int i) {
182 ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[4]);
183 INIT_ADDRESS_RANGE(CheckStackTrace2, start, end, &expected_range[3]);
184 DECLARE_ADDRESS_LABEL(start);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700185 for (int j = i; j >= 0; j--) {
Austin Schuh906616c2019-01-21 20:25:11 -0800186 CheckStackTrace3(j);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700187 }
Austin Schuh906616c2019-01-21 20:25:11 -0800188 DECLARE_ADDRESS_LABEL(end);
189}
190static void ATTRIBUTE_NOINLINE CheckStackTrace1(int i) {
191 ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[5]);
192 INIT_ADDRESS_RANGE(CheckStackTrace1, start, end, &expected_range[4]);
193 DECLARE_ADDRESS_LABEL(start);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700194 for (int j = i; j >= 0; j--) {
Austin Schuh906616c2019-01-21 20:25:11 -0800195 CheckStackTrace2(j);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700196 }
Austin Schuh906616c2019-01-21 20:25:11 -0800197 DECLARE_ADDRESS_LABEL(end);
198}
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700199
200#ifndef __GNUC__
201// On non-GNU environment, we use the address of `CheckStackTrace` to
202// guess the address range of this function. This guess is wrong for
203// non-static function on Windows. This is probably because
204// `&CheckStackTrace` returns the address of a trampoline like PLT,
205// not the actual address of `CheckStackTrace`.
206// See https://github.com/google/glog/issues/421 for the detail.
207static
208#endif
Austin Schuh906616c2019-01-21 20:25:11 -0800209void ATTRIBUTE_NOINLINE CheckStackTrace(int i) {
210 INIT_ADDRESS_RANGE(CheckStackTrace, start, end, &expected_range[5]);
211 DECLARE_ADDRESS_LABEL(start);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700212 for (int j = i; j >= 0; j--) {
Austin Schuh906616c2019-01-21 20:25:11 -0800213 CheckStackTrace1(j);
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700214 }
Austin Schuh906616c2019-01-21 20:25:11 -0800215 DECLARE_ADDRESS_LABEL(end);
216}
217
James Kuszmaulba0ac1a2022-08-12 16:29:30 -0700218#if defined(__clang__)
219#pragma clang diagnostic pop
220#endif
221
Austin Schuh906616c2019-01-21 20:25:11 -0800222//-----------------------------------------------------------------------//
223
224int main(int, char ** argv) {
225 FLAGS_logtostderr = true;
226 InitGoogleLogging(argv[0]);
227
228 CheckStackTrace(0);
229
230 printf("PASS\n");
231 return 0;
232}
233
234#else
235int main() {
236 printf("PASS (no stacktrace support)\n");
237 return 0;
238}
239#endif // HAVE_STACKTRACE