Brian Silverman | 20350ac | 2021-11-17 18:19:55 -0800 | [diff] [blame^] | 1 | // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- |
| 2 | // Copyright (c) 2021, gperftools Contributors |
| 3 | // All rights reserved. |
| 4 | // |
| 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
| 8 | // |
| 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. |
| 18 | // |
| 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 | // This file contains "generic" stack frame pointer backtracing |
| 32 | // code. Attempt is made to minimize amount of arch- or os-specific |
| 33 | // code and keep everything as generic as possible. Currently |
| 34 | // supported are x86-64, aarch64 and riscv. |
| 35 | #ifndef BASE_STACKTRACE_GENERIC_FP_INL_H_ |
| 36 | #define BASE_STACKTRACE_GENERIC_FP_INL_H_ |
| 37 | |
| 38 | #if defined(HAVE_SYS_UCONTEXT_H) |
| 39 | #include <sys/ucontext.h> |
| 40 | #elif defined(HAVE_UCONTEXT_H) |
| 41 | #include <ucontext.h> |
| 42 | #endif |
| 43 | |
| 44 | // This is only used on OS-es with mmap support. |
| 45 | #include <sys/mman.h> |
| 46 | |
| 47 | // Set this to true to disable "probing" of addresses that are read to |
| 48 | // make backtracing less-safe, but faster. |
| 49 | #ifndef TCMALLOC_UNSAFE_GENERIC_FP_STACKTRACE |
| 50 | #define TCMALLOC_UNSAFE_GENERIC_FP_STACKTRACE 0 |
| 51 | #endif |
| 52 | |
| 53 | namespace { |
| 54 | namespace stacktrace_generic_fp { |
| 55 | |
| 56 | struct frame { |
| 57 | uintptr_t parent; |
| 58 | void* pc; |
| 59 | }; |
| 60 | |
| 61 | frame* adjust_fp(frame* f) { |
| 62 | #ifdef __riscv |
| 63 | return f - 1; |
| 64 | #else |
| 65 | return f; |
| 66 | #endif |
| 67 | } |
| 68 | |
| 69 | static bool CheckPageIsReadable(void* ptr, void* checked_ptr) { |
| 70 | static uintptr_t pagesize; |
| 71 | if (pagesize == 0) { |
| 72 | pagesize = getpagesize(); |
| 73 | } |
| 74 | |
| 75 | uintptr_t addr = reinterpret_cast<uintptr_t>(ptr); |
| 76 | uintptr_t parent_frame = reinterpret_cast<uintptr_t>(checked_ptr); |
| 77 | |
| 78 | parent_frame &= ~(pagesize - 1); |
| 79 | addr &= ~(pagesize - 1); |
| 80 | |
| 81 | if (parent_frame != 0 && addr == parent_frame) { |
| 82 | return true; |
| 83 | } |
| 84 | |
| 85 | return (msync(reinterpret_cast<void*>(addr), pagesize, MS_ASYNC) == 0); |
| 86 | } |
| 87 | |
| 88 | ATTRIBUTE_NOINLINE // forces architectures with link register to save it |
| 89 | int capture(void **result, int max_depth, int skip_count, |
| 90 | void* initial_frame, void* const * initial_pc) { |
| 91 | int i = 0; |
| 92 | |
| 93 | if (initial_pc != nullptr) { |
| 94 | // This is 'with ucontext' case. We take first pc from ucontext |
| 95 | // and then skip_count is ignored as we assume that caller only |
| 96 | // needed stack trace up to signal handler frame. |
| 97 | skip_count = 0; |
| 98 | if (max_depth == 0) { |
| 99 | return 0; |
| 100 | } |
| 101 | result[0] = *initial_pc; |
| 102 | i++; |
| 103 | } |
| 104 | |
| 105 | constexpr uintptr_t kTooSmallAddr = 16 << 10; |
| 106 | constexpr uintptr_t kFrameSizeThreshold = 128 << 10; |
| 107 | |
| 108 | // This is simplistic yet. Here we're targeting x86-64, aarch64 and |
| 109 | // riscv. All have 16 bytes stack alignment (even 32 bit |
| 110 | // riscv). This can be made more elaborate as we consider more |
| 111 | // architectures. Note, it allows us to only readability of check |
| 112 | // f->parent address. |
| 113 | constexpr uintptr_t kAlignment = 16; |
| 114 | |
| 115 | uintptr_t initial_frame_addr = reinterpret_cast<uintptr_t>(initial_frame); |
| 116 | if ((initial_frame_addr & (kAlignment - 1)) != 0) { |
| 117 | return i; |
| 118 | } |
| 119 | if (initial_frame_addr < kTooSmallAddr) { |
| 120 | return i; |
| 121 | } |
| 122 | |
| 123 | frame* prev_f = nullptr; |
| 124 | frame *f = adjust_fp(reinterpret_cast<frame*>(initial_frame)); |
| 125 | |
| 126 | while (i < max_depth) { |
| 127 | if (!TCMALLOC_UNSAFE_GENERIC_FP_STACKTRACE |
| 128 | && !CheckPageIsReadable(&f->parent, prev_f)) { |
| 129 | break; |
| 130 | } |
| 131 | |
| 132 | void* pc = f->pc; |
| 133 | if (pc == nullptr) { |
| 134 | break; |
| 135 | } |
| 136 | |
| 137 | if (i >= skip_count) { |
| 138 | result[i - skip_count] = pc; |
| 139 | } |
| 140 | |
| 141 | i++; |
| 142 | |
| 143 | uintptr_t parent_frame_addr = f->parent; |
| 144 | uintptr_t child_frame_addr = reinterpret_cast<uintptr_t>(f); |
| 145 | |
| 146 | if (parent_frame_addr < kTooSmallAddr) { |
| 147 | break; |
| 148 | } |
| 149 | // stack grows towards smaller addresses, so if we didn't see |
| 150 | // frame address increased (going from child to parent), it is bad |
| 151 | // frame. We also test if frame is too big since that is another |
| 152 | // sign of bad stack frame. |
| 153 | if (parent_frame_addr - child_frame_addr > kFrameSizeThreshold) { |
| 154 | break; |
| 155 | } |
| 156 | |
| 157 | if ((parent_frame_addr & (kAlignment - 1)) != 0) { |
| 158 | // not aligned, so we keep it safe and assume frame is bogus |
| 159 | break; |
| 160 | } |
| 161 | |
| 162 | prev_f = f; |
| 163 | |
| 164 | f = adjust_fp(reinterpret_cast<frame*>(parent_frame_addr)); |
| 165 | } |
| 166 | return i; |
| 167 | } |
| 168 | |
| 169 | } // namespace stacktrace_generic_fp |
| 170 | } // namespace |
| 171 | |
| 172 | #endif // BASE_STACKTRACE_GENERIC_FP_INL_H_ |
| 173 | |
| 174 | // Note: this part of the file is included several times. |
| 175 | // Do not put globals below. |
| 176 | |
| 177 | // The following 4 functions are generated from the code below: |
| 178 | // GetStack{Trace,Frames}() |
| 179 | // GetStack{Trace,Frames}WithContext() |
| 180 | // |
| 181 | // These functions take the following args: |
| 182 | // void** result: the stack-trace, as an array |
| 183 | // int* sizes: the size of each stack frame, as an array |
| 184 | // (GetStackFrames* only) |
| 185 | // int max_depth: the size of the result (and sizes) array(s) |
| 186 | // int skip_count: how many stack pointers to skip before storing in result |
| 187 | // void* ucp: a ucontext_t* (GetStack{Trace,Frames}WithContext only) |
| 188 | |
| 189 | static int GET_STACK_TRACE_OR_FRAMES { |
| 190 | #if IS_STACK_FRAMES |
| 191 | memset(sizes, 0, sizeof(*sizes) * max_depth); |
| 192 | #endif |
| 193 | |
| 194 | // one for this function |
| 195 | skip_count += 1; |
| 196 | |
| 197 | void* const * initial_pc = nullptr; |
| 198 | void* initial_frame = __builtin_frame_address(0); |
| 199 | |
| 200 | #if IS_WITH_CONTEXT |
| 201 | if (ucp) { |
| 202 | auto uc = static_cast<const ucontext_t*>(ucp); |
| 203 | #ifdef __riscv |
| 204 | initial_pc = reinterpret_cast<void* const *>(&uc->uc_mcontext.__gregs[REG_PC]); |
| 205 | initial_frame = reinterpret_cast<void*>(uc->uc_mcontext.__gregs[REG_S0]); |
| 206 | #elif __aarch64__ |
| 207 | initial_pc = reinterpret_cast<void* const *>(&uc->uc_mcontext.pc); |
| 208 | initial_frame = reinterpret_cast<void*>(uc->uc_mcontext.regs[29]); |
| 209 | #else |
| 210 | initial_pc = reinterpret_cast<void* const *>(&uc->uc_mcontext.gregs[REG_RIP]); |
| 211 | initial_frame = reinterpret_cast<void*>(uc->uc_mcontext.gregs[REG_RBP]); |
| 212 | #endif |
| 213 | } |
| 214 | #endif // IS_WITH_CONTEXT |
| 215 | |
| 216 | int n = stacktrace_generic_fp::capture(result, max_depth, skip_count, |
| 217 | initial_frame, initial_pc); |
| 218 | |
| 219 | // make sure we don't tail-call capture |
| 220 | (void)*(const_cast<void * volatile *>(result)); |
| 221 | return n; |
| 222 | } |