Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2006, Google Inc. |
michael@0 | 2 | // All rights reserved. |
michael@0 | 3 | // |
michael@0 | 4 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | // modification, are permitted provided that the following conditions are |
michael@0 | 6 | // met: |
michael@0 | 7 | // |
michael@0 | 8 | // * Redistributions of source code must retain the above copyright |
michael@0 | 9 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | // * Redistributions in binary form must reproduce the above |
michael@0 | 11 | // copyright notice, this list of conditions and the following disclaimer |
michael@0 | 12 | // in the documentation and/or other materials provided with the |
michael@0 | 13 | // distribution. |
michael@0 | 14 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 15 | // contributors may be used to endorse or promote products derived from |
michael@0 | 16 | // this software without specific prior written permission. |
michael@0 | 17 | // |
michael@0 | 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 29 | |
michael@0 | 30 | // stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the |
michael@0 | 31 | // running process' stack as test data, if running on an x86 or ppc and |
michael@0 | 32 | // compiled with gcc. This test is not enabled in the "make check" suite |
michael@0 | 33 | // by default, because certain optimizations interfere with its proper |
michael@0 | 34 | // operation. To turn it on, configure with --enable-selftest. |
michael@0 | 35 | // |
michael@0 | 36 | // Optimizations that cause problems: |
michael@0 | 37 | // - stack frame reuse. The Recursor function here calls itself with |
michael@0 | 38 | // |return Recursor|. When the caller's frame is reused, it will cause |
michael@0 | 39 | // CountCallerFrames to correctly return the same number of frames |
michael@0 | 40 | // in both the caller and callee. This is considered an unexpected |
michael@0 | 41 | // condition in the test, which expects a callee to have one more |
michael@0 | 42 | // caller frame in the stack than its caller. |
michael@0 | 43 | // - frame pointer omission. Even with a stackwalker that understands |
michael@0 | 44 | // this optimization, the code to harness debug information currently |
michael@0 | 45 | // only exists to retrieve it from minidumps, not the current process. |
michael@0 | 46 | // |
michael@0 | 47 | // This test can also serve as a developmental and debugging aid if |
michael@0 | 48 | // PRINT_STACKS is defined. |
michael@0 | 49 | // |
michael@0 | 50 | // Author: Mark Mentovai |
michael@0 | 51 | |
michael@0 | 52 | #include "processor/logging.h" |
michael@0 | 53 | |
michael@0 | 54 | #if defined(__i386) && !defined(__i386__) |
michael@0 | 55 | #define __i386__ |
michael@0 | 56 | #endif |
michael@0 | 57 | #if defined(__sparc) && !defined(__sparc__) |
michael@0 | 58 | #define __sparc__ |
michael@0 | 59 | #endif |
michael@0 | 60 | |
michael@0 | 61 | #if (defined(__SUNPRO_CC) || defined(__GNUC__)) && \ |
michael@0 | 62 | (defined(__i386__) || defined(__ppc__) || defined(__sparc__)) |
michael@0 | 63 | |
michael@0 | 64 | |
michael@0 | 65 | #include <stdio.h> |
michael@0 | 66 | |
michael@0 | 67 | #include "common/scoped_ptr.h" |
michael@0 | 68 | #include "google_breakpad/common/breakpad_types.h" |
michael@0 | 69 | #include "google_breakpad/common/minidump_format.h" |
michael@0 | 70 | #include "google_breakpad/processor/basic_source_line_resolver.h" |
michael@0 | 71 | #include "google_breakpad/processor/call_stack.h" |
michael@0 | 72 | #include "google_breakpad/processor/code_module.h" |
michael@0 | 73 | #include "google_breakpad/processor/memory_region.h" |
michael@0 | 74 | #include "google_breakpad/processor/stack_frame.h" |
michael@0 | 75 | #include "google_breakpad/processor/stack_frame_cpu.h" |
michael@0 | 76 | |
michael@0 | 77 | using google_breakpad::BasicSourceLineResolver; |
michael@0 | 78 | using google_breakpad::CallStack; |
michael@0 | 79 | using google_breakpad::CodeModule; |
michael@0 | 80 | using google_breakpad::MemoryRegion; |
michael@0 | 81 | using google_breakpad::scoped_ptr; |
michael@0 | 82 | using google_breakpad::StackFrame; |
michael@0 | 83 | using google_breakpad::StackFramePPC; |
michael@0 | 84 | using google_breakpad::StackFrameX86; |
michael@0 | 85 | using google_breakpad::StackFrameSPARC; |
michael@0 | 86 | |
michael@0 | 87 | #if defined(__i386__) |
michael@0 | 88 | #include "processor/stackwalker_x86.h" |
michael@0 | 89 | using google_breakpad::StackwalkerX86; |
michael@0 | 90 | #elif defined(__ppc__) |
michael@0 | 91 | #include "processor/stackwalker_ppc.h" |
michael@0 | 92 | using google_breakpad::StackwalkerPPC; |
michael@0 | 93 | #elif defined(__sparc__) |
michael@0 | 94 | #include "processor/stackwalker_sparc.h" |
michael@0 | 95 | using google_breakpad::StackwalkerSPARC; |
michael@0 | 96 | #endif // __i386__ || __ppc__ || __sparc__ |
michael@0 | 97 | |
michael@0 | 98 | #define RECURSION_DEPTH 100 |
michael@0 | 99 | |
michael@0 | 100 | |
michael@0 | 101 | // A simple MemoryRegion subclass that provides direct access to this |
michael@0 | 102 | // process' memory space by pointer. |
michael@0 | 103 | class SelfMemoryRegion : public MemoryRegion { |
michael@0 | 104 | public: |
michael@0 | 105 | virtual uint64_t GetBase() { return 0; } |
michael@0 | 106 | virtual uint32_t GetSize() { return 0xffffffff; } |
michael@0 | 107 | |
michael@0 | 108 | bool GetMemoryAtAddress(uint64_t address, uint8_t* value) { |
michael@0 | 109 | return GetMemoryAtAddressInternal(address, value); } |
michael@0 | 110 | bool GetMemoryAtAddress(uint64_t address, uint16_t* value) { |
michael@0 | 111 | return GetMemoryAtAddressInternal(address, value); } |
michael@0 | 112 | bool GetMemoryAtAddress(uint64_t address, uint32_t* value) { |
michael@0 | 113 | return GetMemoryAtAddressInternal(address, value); } |
michael@0 | 114 | bool GetMemoryAtAddress(uint64_t address, uint64_t* value) { |
michael@0 | 115 | return GetMemoryAtAddressInternal(address, value); } |
michael@0 | 116 | |
michael@0 | 117 | private: |
michael@0 | 118 | template<typename T> bool GetMemoryAtAddressInternal(uint64_t address, |
michael@0 | 119 | T* value) { |
michael@0 | 120 | // Without knowing what addresses are actually mapped, just assume that |
michael@0 | 121 | // everything low is not mapped. This helps the stackwalker catch the |
michael@0 | 122 | // end of a stack when it tries to dereference a null or low pointer |
michael@0 | 123 | // in an attempt to find the caller frame. Other unmapped accesses will |
michael@0 | 124 | // cause the program to crash, but that would properly be a test failure. |
michael@0 | 125 | if (address < 0x100) |
michael@0 | 126 | return false; |
michael@0 | 127 | |
michael@0 | 128 | uint8_t* memory = 0; |
michael@0 | 129 | *value = *reinterpret_cast<const T*>(&memory[address]); |
michael@0 | 130 | return true; |
michael@0 | 131 | } |
michael@0 | 132 | }; |
michael@0 | 133 | |
michael@0 | 134 | |
michael@0 | 135 | #if defined(__GNUC__) |
michael@0 | 136 | |
michael@0 | 137 | |
michael@0 | 138 | #if defined(__i386__) |
michael@0 | 139 | |
michael@0 | 140 | // GetEBP returns the current value of the %ebp register. Because it's |
michael@0 | 141 | // implemented as a function, %ebp itself contains GetEBP's frame pointer |
michael@0 | 142 | // and not the caller's frame pointer. Dereference %ebp to obtain the |
michael@0 | 143 | // caller's frame pointer, which the compiler-generated preamble stored |
michael@0 | 144 | // on the stack (provided frame pointers are not being omitted.) Because |
michael@0 | 145 | // this function depends on the compiler-generated preamble, inlining is |
michael@0 | 146 | // disabled. |
michael@0 | 147 | static uint32_t GetEBP() __attribute__((noinline)); |
michael@0 | 148 | static uint32_t GetEBP() { |
michael@0 | 149 | uint32_t ebp; |
michael@0 | 150 | __asm__ __volatile__( |
michael@0 | 151 | "movl (%%ebp), %0" |
michael@0 | 152 | : "=a" (ebp) |
michael@0 | 153 | ); |
michael@0 | 154 | return ebp; |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | |
michael@0 | 158 | // The caller's %esp is 8 higher than the value of %ebp in this function, |
michael@0 | 159 | // assuming that it's not inlined and that the standard prolog is used. |
michael@0 | 160 | // The CALL instruction places a 4-byte return address on the stack above |
michael@0 | 161 | // the caller's %esp, and this function's prolog will save the caller's %ebp |
michael@0 | 162 | // on the stack as well, for another 4 bytes, before storing %esp in %ebp. |
michael@0 | 163 | static uint32_t GetESP() __attribute__((noinline)); |
michael@0 | 164 | static uint32_t GetESP() { |
michael@0 | 165 | uint32_t ebp; |
michael@0 | 166 | __asm__ __volatile__( |
michael@0 | 167 | "movl %%ebp, %0" |
michael@0 | 168 | : "=a" (ebp) |
michael@0 | 169 | ); |
michael@0 | 170 | return ebp + 8; |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | |
michael@0 | 174 | // GetEIP returns the instruction pointer identifying the next instruction |
michael@0 | 175 | // to execute after GetEIP returns. It obtains this information from the |
michael@0 | 176 | // stack, where it was placed by the call instruction that called GetEIP. |
michael@0 | 177 | // This function depends on frame pointers not being omitted. It is possible |
michael@0 | 178 | // to write a pure asm version of this routine that has no compiler-generated |
michael@0 | 179 | // preamble and uses %esp instead of %ebp; that would function in the |
michael@0 | 180 | // absence of frame pointers. However, the simpler approach is used here |
michael@0 | 181 | // because GetEBP and stackwalking necessarily depends on access to frame |
michael@0 | 182 | // pointers. Because this function depends on a call instruction and the |
michael@0 | 183 | // compiler-generated preamble, inlining is disabled. |
michael@0 | 184 | static uint32_t GetEIP() __attribute__((noinline)); |
michael@0 | 185 | static uint32_t GetEIP() { |
michael@0 | 186 | uint32_t eip; |
michael@0 | 187 | __asm__ __volatile__( |
michael@0 | 188 | "movl 4(%%ebp), %0" |
michael@0 | 189 | : "=a" (eip) |
michael@0 | 190 | ); |
michael@0 | 191 | return eip; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | |
michael@0 | 195 | #elif defined(__ppc__) |
michael@0 | 196 | |
michael@0 | 197 | |
michael@0 | 198 | // GetSP returns the current value of the %r1 register, which by convention, |
michael@0 | 199 | // is the stack pointer on ppc. Because it's implemented as a function, |
michael@0 | 200 | // %r1 itself contains GetSP's own stack pointer and not the caller's stack |
michael@0 | 201 | // pointer. Dereference %r1 to obtain the caller's stack pointer, which the |
michael@0 | 202 | // compiler-generated prolog stored on the stack. Because this function |
michael@0 | 203 | // depends on the compiler-generated prolog, inlining is disabled. |
michael@0 | 204 | static uint32_t GetSP() __attribute__((noinline)); |
michael@0 | 205 | static uint32_t GetSP() { |
michael@0 | 206 | uint32_t sp; |
michael@0 | 207 | __asm__ __volatile__( |
michael@0 | 208 | "lwz %0, 0(r1)" |
michael@0 | 209 | : "=r" (sp) |
michael@0 | 210 | ); |
michael@0 | 211 | return sp; |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | |
michael@0 | 215 | // GetPC returns the program counter identifying the next instruction to |
michael@0 | 216 | // execute after GetPC returns. It obtains this information from the |
michael@0 | 217 | // link register, where it was placed by the branch instruction that called |
michael@0 | 218 | // GetPC. Because this function depends on the caller's use of a branch |
michael@0 | 219 | // instruction, inlining is disabled. |
michael@0 | 220 | static uint32_t GetPC() __attribute__((noinline)); |
michael@0 | 221 | static uint32_t GetPC() { |
michael@0 | 222 | uint32_t lr; |
michael@0 | 223 | __asm__ __volatile__( |
michael@0 | 224 | "mflr %0" |
michael@0 | 225 | : "=r" (lr) |
michael@0 | 226 | ); |
michael@0 | 227 | return lr; |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | |
michael@0 | 231 | #elif defined(__sparc__) |
michael@0 | 232 | |
michael@0 | 233 | |
michael@0 | 234 | // GetSP returns the current value of the %sp/%o6/%g_r[14] register, which |
michael@0 | 235 | // by convention, is the stack pointer on sparc. Because it's implemented |
michael@0 | 236 | // as a function, %sp itself contains GetSP's own stack pointer and not |
michael@0 | 237 | // the caller's stack pointer. Dereference to obtain the caller's stack |
michael@0 | 238 | // pointer, which the compiler-generated prolog stored on the stack. |
michael@0 | 239 | // Because this function depends on the compiler-generated prolog, inlining |
michael@0 | 240 | // is disabled. |
michael@0 | 241 | static uint32_t GetSP() __attribute__((noinline)); |
michael@0 | 242 | static uint32_t GetSP() { |
michael@0 | 243 | uint32_t sp; |
michael@0 | 244 | __asm__ __volatile__( |
michael@0 | 245 | "mov %%fp, %0" |
michael@0 | 246 | : "=r" (sp) |
michael@0 | 247 | ); |
michael@0 | 248 | return sp; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | // GetFP returns the current value of the %fp register. Because it's |
michael@0 | 252 | // implemented as a function, %fp itself contains GetFP's frame pointer |
michael@0 | 253 | // and not the caller's frame pointer. Dereference %fp to obtain the |
michael@0 | 254 | // caller's frame pointer, which the compiler-generated preamble stored |
michael@0 | 255 | // on the stack (provided frame pointers are not being omitted.) Because |
michael@0 | 256 | // this function depends on the compiler-generated preamble, inlining is |
michael@0 | 257 | // disabled. |
michael@0 | 258 | static uint32_t GetFP() __attribute__((noinline)); |
michael@0 | 259 | static uint32_t GetFP() { |
michael@0 | 260 | uint32_t fp; |
michael@0 | 261 | __asm__ __volatile__( |
michael@0 | 262 | "ld [%%fp+56], %0" |
michael@0 | 263 | : "=r" (fp) |
michael@0 | 264 | ); |
michael@0 | 265 | return fp; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | // GetPC returns the program counter identifying the next instruction to |
michael@0 | 269 | // execute after GetPC returns. It obtains this information from the |
michael@0 | 270 | // link register, where it was placed by the branch instruction that called |
michael@0 | 271 | // GetPC. Because this function depends on the caller's use of a branch |
michael@0 | 272 | // instruction, inlining is disabled. |
michael@0 | 273 | static uint32_t GetPC() __attribute__((noinline)); |
michael@0 | 274 | static uint32_t GetPC() { |
michael@0 | 275 | uint32_t pc; |
michael@0 | 276 | __asm__ __volatile__( |
michael@0 | 277 | "mov %%i7, %0" |
michael@0 | 278 | : "=r" (pc) |
michael@0 | 279 | ); |
michael@0 | 280 | return pc + 8; |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | #endif // __i386__ || __ppc__ || __sparc__ |
michael@0 | 284 | |
michael@0 | 285 | #elif defined(__SUNPRO_CC) |
michael@0 | 286 | |
michael@0 | 287 | #if defined(__i386__) |
michael@0 | 288 | extern "C" { |
michael@0 | 289 | extern uint32_t GetEIP(); |
michael@0 | 290 | extern uint32_t GetEBP(); |
michael@0 | 291 | extern uint32_t GetESP(); |
michael@0 | 292 | } |
michael@0 | 293 | #elif defined(__sparc__) |
michael@0 | 294 | extern "C" { |
michael@0 | 295 | extern uint32_t GetPC(); |
michael@0 | 296 | extern uint32_t GetFP(); |
michael@0 | 297 | extern uint32_t GetSP(); |
michael@0 | 298 | } |
michael@0 | 299 | #endif // __i386__ || __sparc__ |
michael@0 | 300 | |
michael@0 | 301 | #endif // __GNUC__ || __SUNPRO_CC |
michael@0 | 302 | |
michael@0 | 303 | // CountCallerFrames returns the number of stack frames beneath the function |
michael@0 | 304 | // that called CountCallerFrames. Because this function's return value |
michael@0 | 305 | // is dependent on the size of the stack beneath it, inlining is disabled, |
michael@0 | 306 | // and any function that calls this should not be inlined either. |
michael@0 | 307 | #if defined(__GNUC__) |
michael@0 | 308 | static unsigned int CountCallerFrames() __attribute__((noinline)); |
michael@0 | 309 | #elif defined(__SUNPRO_CC) |
michael@0 | 310 | static unsigned int CountCallerFrames(); |
michael@0 | 311 | #endif |
michael@0 | 312 | static unsigned int CountCallerFrames() { |
michael@0 | 313 | SelfMemoryRegion memory; |
michael@0 | 314 | BasicSourceLineResolver resolver; |
michael@0 | 315 | |
michael@0 | 316 | #if defined(__i386__) |
michael@0 | 317 | MDRawContextX86 context = MDRawContextX86(); |
michael@0 | 318 | context.eip = GetEIP(); |
michael@0 | 319 | context.ebp = GetEBP(); |
michael@0 | 320 | context.esp = GetESP(); |
michael@0 | 321 | |
michael@0 | 322 | StackwalkerX86 stackwalker = StackwalkerX86(NULL, &context, &memory, NULL, |
michael@0 | 323 | NULL, &resolver); |
michael@0 | 324 | #elif defined(__ppc__) |
michael@0 | 325 | MDRawContextPPC context = MDRawContextPPC(); |
michael@0 | 326 | context.srr0 = GetPC(); |
michael@0 | 327 | context.gpr[1] = GetSP(); |
michael@0 | 328 | |
michael@0 | 329 | StackwalkerPPC stackwalker = StackwalkerPPC(NULL, &context, &memory, NULL, |
michael@0 | 330 | NULL, &resolver); |
michael@0 | 331 | #elif defined(__sparc__) |
michael@0 | 332 | MDRawContextSPARC context = MDRawContextSPARC(); |
michael@0 | 333 | context.pc = GetPC(); |
michael@0 | 334 | context.g_r[14] = GetSP(); |
michael@0 | 335 | context.g_r[30] = GetFP(); |
michael@0 | 336 | |
michael@0 | 337 | StackwalkerSPARC stackwalker = StackwalkerSPARC(NULL, &context, &memory, |
michael@0 | 338 | NULL, NULL, &resolver); |
michael@0 | 339 | #endif // __i386__ || __ppc__ || __sparc__ |
michael@0 | 340 | |
michael@0 | 341 | CallStack stack; |
michael@0 | 342 | vector<const CodeModule*> modules_without_symbols; |
michael@0 | 343 | stackwalker.Walk(&stack, &modules_without_symbols); |
michael@0 | 344 | |
michael@0 | 345 | #ifdef PRINT_STACKS |
michael@0 | 346 | printf("\n"); |
michael@0 | 347 | for (unsigned int frame_index = 0; |
michael@0 | 348 | frame_index < stack.frames()->size(); |
michael@0 | 349 | ++frame_index) { |
michael@0 | 350 | StackFrame *frame = stack.frames()->at(frame_index); |
michael@0 | 351 | printf("frame %-3d instruction = 0x%08" PRIx64, |
michael@0 | 352 | frame_index, frame->instruction); |
michael@0 | 353 | #if defined(__i386__) |
michael@0 | 354 | StackFrameX86 *frame_x86 = reinterpret_cast<StackFrameX86*>(frame); |
michael@0 | 355 | printf(" esp = 0x%08x ebp = 0x%08x\n", |
michael@0 | 356 | frame_x86->context.esp, frame_x86->context.ebp); |
michael@0 | 357 | #elif defined(__ppc__) |
michael@0 | 358 | StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame); |
michael@0 | 359 | printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]); |
michael@0 | 360 | #elif defined(__sparc__) |
michael@0 | 361 | StackFrameSPARC *frame_sparc = reinterpret_cast<StackFrameSPARC*>(frame); |
michael@0 | 362 | printf(" sp = 0x%08x fp = 0x%08x\n", |
michael@0 | 363 | frame_sparc->context.g_r[14], frame_sparc->context.g_r[30]); |
michael@0 | 364 | #endif // __i386__ || __ppc__ || __sparc__ |
michael@0 | 365 | } |
michael@0 | 366 | #endif // PRINT_STACKS |
michael@0 | 367 | |
michael@0 | 368 | // Subtract 1 because the caller wants the number of frames beneath |
michael@0 | 369 | // itself. Because the caller called us, subract two for our frame and its |
michael@0 | 370 | // frame, which are included in stack.size(). |
michael@0 | 371 | return stack.frames()->size() - 2; |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | |
michael@0 | 375 | // Recursor verifies that the number stack frames beneath itself is one more |
michael@0 | 376 | // than the number of stack frames beneath its parent. When depth frames |
michael@0 | 377 | // have been reached, Recursor stops checking and returns success. If the |
michael@0 | 378 | // frame count check fails at any depth, Recursor will stop and return false. |
michael@0 | 379 | // Because this calls CountCallerFrames, inlining is disabled. |
michael@0 | 380 | #if defined(__GNUC__) |
michael@0 | 381 | static bool Recursor(unsigned int depth, unsigned int parent_callers) |
michael@0 | 382 | __attribute__((noinline)); |
michael@0 | 383 | #elif defined(__SUNPRO_CC) |
michael@0 | 384 | static bool Recursor(unsigned int depth, unsigned int parent_callers); |
michael@0 | 385 | #endif |
michael@0 | 386 | static bool Recursor(unsigned int depth, unsigned int parent_callers) { |
michael@0 | 387 | unsigned int callers = CountCallerFrames(); |
michael@0 | 388 | if (callers != parent_callers + 1) |
michael@0 | 389 | return false; |
michael@0 | 390 | |
michael@0 | 391 | if (depth) |
michael@0 | 392 | return Recursor(depth - 1, callers); |
michael@0 | 393 | |
michael@0 | 394 | // depth == 0 |
michael@0 | 395 | return true; |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | |
michael@0 | 399 | // Because this calls CountCallerFrames, inlining is disabled - but because |
michael@0 | 400 | // it's main (and nobody calls it other than the entry point), it wouldn't |
michael@0 | 401 | // be inlined anyway. |
michael@0 | 402 | #if defined(__GNUC__) |
michael@0 | 403 | int main(int argc, char** argv) __attribute__((noinline)); |
michael@0 | 404 | #elif defined(__SUNPRO_CC) |
michael@0 | 405 | int main(int argc, char** argv); |
michael@0 | 406 | #endif |
michael@0 | 407 | int main(int argc, char** argv) { |
michael@0 | 408 | BPLOG_INIT(&argc, &argv); |
michael@0 | 409 | |
michael@0 | 410 | return Recursor(RECURSION_DEPTH, CountCallerFrames()) ? 0 : 1; |
michael@0 | 411 | } |
michael@0 | 412 | |
michael@0 | 413 | |
michael@0 | 414 | #else |
michael@0 | 415 | // Not i386 or ppc or sparc? We can only test stacks we know how to walk. |
michael@0 | 416 | |
michael@0 | 417 | |
michael@0 | 418 | int main(int argc, char **argv) { |
michael@0 | 419 | BPLOG_INIT(&argc, &argv); |
michael@0 | 420 | |
michael@0 | 421 | // "make check" interprets an exit status of 77 to mean that the test is |
michael@0 | 422 | // not supported. |
michael@0 | 423 | BPLOG(ERROR) << "Selftest not supported here"; |
michael@0 | 424 | return 77; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | |
michael@0 | 428 | #endif // (__GNUC__ || __SUNPRO_CC) && (__i386__ || __ppc__ || __sparc__) |