Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 1 | // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 2 | // Copyright (c) 2005, Google Inc. |
| 3 | // All rights reserved. |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 4 | // |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 8 | // |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 9 | // * Redistributions of source code must retain the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer. |
| 11 | // * Redistributions in binary form must reproduce the above |
| 12 | // copyright notice, this list of conditions and the following disclaimer |
| 13 | // in the documentation and/or other materials provided with the |
| 14 | // distribution. |
| 15 | // * Neither the name of Google Inc. nor the names of its |
| 16 | // contributors may be used to endorse or promote products derived from |
| 17 | // this software without specific prior written permission. |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 18 | // |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | #include "config_for_unittests.h" |
| 32 | #ifdef HAVE_EXECINFO_H |
| 33 | #include <execinfo.h> |
| 34 | #endif |
| 35 | #include <stdio.h> |
| 36 | #include <stdlib.h> |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 37 | |
| 38 | // On those architectures we can and should test if backtracing with |
| 39 | // ucontext and from signal handler works |
| 40 | #if __GNUC__ && __linux__ && (__x86_64__ || __aarch64__ || __riscv) |
| 41 | #include <signal.h> |
| 42 | #define TEST_UCONTEXT_BITS 1 |
| 43 | #endif |
| 44 | |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 45 | #include "base/commandlineflags.h" |
| 46 | #include "base/logging.h" |
| 47 | #include <gperftools/stacktrace.h> |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 48 | #include "tests/testutil.h" |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 49 | |
| 50 | namespace { |
| 51 | |
| 52 | // Obtain a backtrace, verify that the expected callers are present in the |
| 53 | // backtrace, and maybe print the backtrace to stdout. |
| 54 | |
| 55 | // The sequence of functions whose return addresses we expect to see in the |
| 56 | // backtrace. |
| 57 | const int BACKTRACE_STEPS = 6; |
| 58 | |
| 59 | struct AddressRange { |
| 60 | const void *start, *end; |
| 61 | }; |
| 62 | |
| 63 | // Expected function [start,end] range. |
| 64 | AddressRange expected_range[BACKTRACE_STEPS]; |
| 65 | |
| 66 | #if __GNUC__ |
| 67 | // Using GCC extension: address of a label can be taken with '&&label'. |
| 68 | // Start should be a label somewhere before recursive call, end somewhere |
| 69 | // after it. |
| 70 | #define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \ |
| 71 | do { \ |
| 72 | (prange)->start = &&start_label; \ |
| 73 | (prange)->end = &&end_label; \ |
| 74 | CHECK_LT((prange)->start, (prange)->end); \ |
| 75 | } while (0) |
| 76 | // This macro expands into "unmovable" code (opaque to GCC), and that |
| 77 | // prevents GCC from moving a_label up or down in the code. |
| 78 | // Without it, there is no code following the 'end' label, and GCC |
| 79 | // (4.3.1, 4.4.0) thinks it safe to assign &&end an address that is before |
| 80 | // the recursive call. |
| 81 | #define DECLARE_ADDRESS_LABEL(a_label) \ |
| 82 | a_label: do { __asm__ __volatile__(""); } while (0) |
| 83 | // Gcc 4.4.0 may split function into multiple chunks, and the chunk |
| 84 | // performing recursive call may end up later in the code then the return |
| 85 | // instruction (this actually happens with FDO). |
| 86 | // Adjust function range from __builtin_return_address. |
| 87 | #define ADJUST_ADDRESS_RANGE_FROM_RA(prange) \ |
| 88 | do { \ |
| 89 | void *ra = __builtin_return_address(0); \ |
| 90 | CHECK_LT((prange)->start, ra); \ |
| 91 | if (ra > (prange)->end) { \ |
| 92 | printf("Adjusting range from %p..%p to %p..%p\n", \ |
| 93 | (prange)->start, (prange)->end, \ |
| 94 | (prange)->start, ra); \ |
| 95 | (prange)->end = ra; \ |
| 96 | } \ |
| 97 | } while (0) |
| 98 | #else |
| 99 | // Assume the Check* functions below are not longer than 256 bytes. |
| 100 | #define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \ |
| 101 | do { \ |
| 102 | (prange)->start = reinterpret_cast<const void *>(&fn); \ |
| 103 | (prange)->end = reinterpret_cast<const char *>(&fn) + 256; \ |
| 104 | } while (0) |
| 105 | #define DECLARE_ADDRESS_LABEL(a_label) do { } while (0) |
| 106 | #define ADJUST_ADDRESS_RANGE_FROM_RA(prange) do { } while (0) |
| 107 | #endif // __GNUC__ |
| 108 | |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 109 | |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 110 | //-----------------------------------------------------------------------// |
| 111 | |
| 112 | void CheckRetAddrIsInFunction(void *ret_addr, const AddressRange &range) |
| 113 | { |
| 114 | CHECK_GE(ret_addr, range.start); |
| 115 | CHECK_LE(ret_addr, range.end); |
| 116 | } |
| 117 | |
| 118 | //-----------------------------------------------------------------------// |
| 119 | |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 120 | #if TEST_UCONTEXT_BITS |
| 121 | |
| 122 | struct get_stack_trace_args { |
| 123 | int *size_ptr; |
| 124 | void **result; |
| 125 | int max_depth; |
| 126 | uintptr_t where; |
| 127 | } gst_args; |
| 128 | |
| 129 | static |
| 130 | void SignalHandler(int dummy, siginfo_t *si, void* ucv) { |
| 131 | auto uc = static_cast<ucontext_t*>(ucv); |
| 132 | |
| 133 | #ifdef __riscv |
| 134 | uc->uc_mcontext.__gregs[REG_PC] = gst_args.where; |
| 135 | #elif __aarch64__ |
| 136 | uc->uc_mcontext.pc = gst_args.where; |
| 137 | #else |
| 138 | uc->uc_mcontext.gregs[REG_RIP] = gst_args.where; |
| 139 | #endif |
| 140 | |
| 141 | *gst_args.size_ptr = GetStackTraceWithContext( |
| 142 | gst_args.result, |
| 143 | gst_args.max_depth, |
| 144 | 2, |
| 145 | uc); |
| 146 | } |
| 147 | |
| 148 | int ATTRIBUTE_NOINLINE CaptureLeafUContext(void **stack, int stack_len) { |
| 149 | INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]); |
| 150 | DECLARE_ADDRESS_LABEL(start); |
| 151 | |
| 152 | int size; |
| 153 | |
| 154 | printf("Capturing stack trace from signal's ucontext\n"); |
| 155 | struct sigaction sa; |
| 156 | memset(&sa, 0, sizeof(sa)); |
| 157 | sa.sa_sigaction = SignalHandler; |
| 158 | sa.sa_flags = SA_SIGINFO | SA_RESETHAND; |
| 159 | int rv = sigaction(SIGSEGV, &sa, nullptr); |
| 160 | CHECK(rv == 0); |
| 161 | |
| 162 | gst_args.size_ptr = &size; |
| 163 | gst_args.result = stack; |
| 164 | gst_args.max_depth = stack_len; |
| 165 | gst_args.where = reinterpret_cast<uintptr_t>(noopt(&&after)); |
| 166 | |
| 167 | // now, "write" to null pointer and trigger sigsegv to run signal |
| 168 | // handler. It'll then change PC to after, as if we jumped one line |
| 169 | // below. |
| 170 | *noopt(reinterpret_cast<void**>(0)) = 0; |
| 171 | // this is not reached, but gcc gets really odd if we don't actually |
| 172 | // use computed goto. |
| 173 | static void* jump_target = &&after; |
| 174 | goto *noopt(&jump_target); |
| 175 | |
| 176 | after: |
| 177 | printf("Obtained %d stack frames.\n", size); |
| 178 | CHECK_GE(size, 1); |
| 179 | CHECK_LE(size, stack_len); |
| 180 | |
| 181 | DECLARE_ADDRESS_LABEL(end); |
| 182 | |
| 183 | return size; |
| 184 | } |
| 185 | |
| 186 | #endif // TEST_UCONTEXT_BITS |
| 187 | |
| 188 | int ATTRIBUTE_NOINLINE CaptureLeafPlain(void **stack, int stack_len) { |
| 189 | INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]); |
| 190 | DECLARE_ADDRESS_LABEL(start); |
| 191 | |
| 192 | int size = GetStackTrace(stack, stack_len, 0); |
| 193 | |
| 194 | printf("Obtained %d stack frames.\n", size); |
| 195 | CHECK_GE(size, 1); |
| 196 | CHECK_LE(size, stack_len); |
| 197 | |
| 198 | DECLARE_ADDRESS_LABEL(end); |
| 199 | |
| 200 | return size; |
| 201 | } |
| 202 | |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 203 | void ATTRIBUTE_NOINLINE CheckStackTrace(int); |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 204 | |
| 205 | int (*leaf_capture_fn)(void**, int) = CaptureLeafPlain; |
| 206 | |
| 207 | void ATTRIBUTE_NOINLINE CheckStackTraceLeaf(int i) { |
| 208 | const int STACK_LEN = 20; |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 209 | void *stack[STACK_LEN]; |
| 210 | int size; |
| 211 | |
| 212 | ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]); |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 213 | |
| 214 | size = leaf_capture_fn(stack, STACK_LEN); |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 215 | |
| 216 | #ifdef HAVE_EXECINFO_H |
| 217 | { |
| 218 | char **strings = backtrace_symbols(stack, size); |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 219 | for (int i = 0; i < size; i++) |
| 220 | printf("%s %p\n", strings[i], stack[i]); |
| 221 | printf("CheckStackTrace() addr: %p\n", &CheckStackTrace); |
| 222 | free(strings); |
| 223 | } |
| 224 | #endif |
| 225 | |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 226 | for (int i = 0, j = 0; i < BACKTRACE_STEPS; i++, j++) { |
| 227 | if (i == 1 && j == 1) { |
| 228 | // this is expected to be our function for which we don't |
| 229 | // establish bounds. So skip. |
| 230 | j++; |
| 231 | } |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 232 | printf("Backtrace %d: expected: %p..%p actual: %p ... ", |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 233 | i, expected_range[i].start, expected_range[i].end, stack[j]); |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 234 | fflush(stdout); |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 235 | CheckRetAddrIsInFunction(stack[j], expected_range[i]); |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 236 | printf("OK\n"); |
| 237 | } |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 238 | } |
| 239 | |
| 240 | //-----------------------------------------------------------------------// |
| 241 | |
| 242 | /* Dummy functions to make the backtrace more interesting. */ |
| 243 | void ATTRIBUTE_NOINLINE CheckStackTrace4(int i) { |
| 244 | ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[2]); |
| 245 | INIT_ADDRESS_RANGE(CheckStackTrace4, start, end, &expected_range[1]); |
| 246 | DECLARE_ADDRESS_LABEL(start); |
| 247 | for (int j = i; j >= 0; j--) |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 248 | CheckStackTraceLeaf(j); |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 249 | DECLARE_ADDRESS_LABEL(end); |
| 250 | } |
| 251 | void ATTRIBUTE_NOINLINE CheckStackTrace3(int i) { |
| 252 | ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[3]); |
| 253 | INIT_ADDRESS_RANGE(CheckStackTrace3, start, end, &expected_range[2]); |
| 254 | DECLARE_ADDRESS_LABEL(start); |
| 255 | for (int j = i; j >= 0; j--) |
| 256 | CheckStackTrace4(j); |
| 257 | DECLARE_ADDRESS_LABEL(end); |
| 258 | } |
| 259 | void ATTRIBUTE_NOINLINE CheckStackTrace2(int i) { |
| 260 | ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[4]); |
| 261 | INIT_ADDRESS_RANGE(CheckStackTrace2, start, end, &expected_range[3]); |
| 262 | DECLARE_ADDRESS_LABEL(start); |
| 263 | for (int j = i; j >= 0; j--) |
| 264 | CheckStackTrace3(j); |
| 265 | DECLARE_ADDRESS_LABEL(end); |
| 266 | } |
| 267 | void ATTRIBUTE_NOINLINE CheckStackTrace1(int i) { |
| 268 | ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[5]); |
| 269 | INIT_ADDRESS_RANGE(CheckStackTrace1, start, end, &expected_range[4]); |
| 270 | DECLARE_ADDRESS_LABEL(start); |
| 271 | for (int j = i; j >= 0; j--) |
| 272 | CheckStackTrace2(j); |
| 273 | DECLARE_ADDRESS_LABEL(end); |
| 274 | } |
| 275 | void ATTRIBUTE_NOINLINE CheckStackTrace(int i) { |
| 276 | INIT_ADDRESS_RANGE(CheckStackTrace, start, end, &expected_range[5]); |
| 277 | DECLARE_ADDRESS_LABEL(start); |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 278 | for (int j = i; j >= 0; j--) { |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 279 | CheckStackTrace1(j); |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 280 | } |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 281 | DECLARE_ADDRESS_LABEL(end); |
| 282 | } |
| 283 | |
| 284 | } // namespace |
| 285 | //-----------------------------------------------------------------------// |
| 286 | |
| 287 | int main(int argc, char ** argv) { |
| 288 | CheckStackTrace(0); |
| 289 | printf("PASS\n"); |
Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 290 | |
| 291 | #if TEST_UCONTEXT_BITS |
| 292 | leaf_capture_fn = CaptureLeafUContext; |
| 293 | CheckStackTrace(0); |
| 294 | printf("PASS\n"); |
| 295 | #endif // TEST_UCONTEXT_BITS |
| 296 | |
Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 297 | return 0; |
| 298 | } |