1// Copyright (c) 2011, 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// ---
31// Author: Doug Kwan
32// This is inspired by Craig Silverstein's PowerPC stacktrace code.
33//
34
35#ifndef BASE_STACKTRACE_ARM_INL_H_
36#define BASE_STACKTRACE_ARM_INL_H_
37// Note: this file is included into stacktrace.cc more than once.
38// Anything that should only be defined once should be here:
39
40#include <stdint.h>   // for uintptr_t
41#include "base/basictypes.h"  // for NULL
42#include <gperftools/stacktrace.h>
43
44// WARNING:
45// This only works if all your code is in either ARM or THUMB mode.  With
46// interworking, the frame pointer of the caller can either be in r11 (ARM
47// mode) or r7 (THUMB mode).  A callee only saves the frame pointer of its
48// mode in a fixed location on its stack frame.  If the caller is a different
49// mode, there is no easy way to find the frame pointer.  It can either be
50// still in the designated register or saved on stack along with other callee
51// saved registers.
52
53// Given a pointer to a stack frame, locate and return the calling
54// stackframe, or return NULL if no stackframe can be found. Perform sanity
55// checks (the strictness of which is controlled by the boolean parameter
56// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
57template<bool STRICT_UNWINDING>
58static void **NextStackFrame(void **old_sp) {
59  void **new_sp = (void**) old_sp[-1];
60
61  // Check that the transition from frame pointer old_sp to frame
62  // pointer new_sp isn't clearly bogus
63  if (STRICT_UNWINDING) {
64    // With the stack growing downwards, older stack frame must be
65    // at a greater address that the current one.
66    if (new_sp <= old_sp) return NULL;
67    // Assume stack frames larger than 100,000 bytes are bogus.
68    if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL;
69  } else {
70    // In the non-strict mode, allow discontiguous stack frames.
71    // (alternate-signal-stacks for example).
72    if (new_sp == old_sp) return NULL;
73    // And allow frames upto about 1MB.
74    if ((new_sp > old_sp)
75        && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return NULL;
76  }
77  if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL;
78  return new_sp;
79}
80
81// This ensures that GetStackTrace stes up the Link Register properly.
82#ifdef __GNUC__
83void StacktraceArmDummyFunction() __attribute__((noinline));
84void StacktraceArmDummyFunction() { __asm__ volatile(""); }
85#else
86# error StacktraceArmDummyFunction() needs to be ported to this platform.
87#endif
88#endif  // BASE_STACKTRACE_ARM_INL_H_
89
90// Note: this part of the file is included several times.
91// Do not put globals below.
92
93// The following 4 functions are generated from the code below:
94//   GetStack{Trace,Frames}()
95//   GetStack{Trace,Frames}WithContext()
96//
97// These functions take the following args:
98//   void** result: the stack-trace, as an array
99//   int* sizes: the size of each stack frame, as an array
100//               (GetStackFrames* only)
101//   int max_depth: the size of the result (and sizes) array(s)
102//   int skip_count: how many stack pointers to skip before storing in result
103//   void* ucp: a ucontext_t* (GetStack{Trace,Frames}WithContext only)
104int GET_STACK_TRACE_OR_FRAMES {
105#ifdef __GNUC__
106  void **sp = reinterpret_cast<void**>(__builtin_frame_address(0));
107#else
108# error reading stack point not yet supported on this platform.
109#endif
110
111  // On ARM, the return address is stored in the link register (r14).
112  // This is not saved on the stack frame of a leaf function.  To
113  // simplify code that reads return addresses, we call a dummy
114  // function so that the return address of this function is also
115  // stored in the stack frame.  This works at least for gcc.
116  StacktraceArmDummyFunction();
117
118  int n = 0;
119  while (sp && n < max_depth) {
120    // The GetStackFrames routine is called when we are in some
121    // informational context (the failure signal handler for example).
122    // Use the non-strict unwinding rules to produce a stack trace
123    // that is as complete as possible (even if it contains a few bogus
124    // entries in some rare cases).
125    void **next_sp = NextStackFrame<IS_STACK_FRAMES == 0>(sp);
126
127    if (skip_count > 0) {
128      skip_count--;
129    } else {
130      result[n] = *sp;
131
132#if IS_STACK_FRAMES
133      if (next_sp > sp) {
134        sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
135      } else {
136        // A frame-size of 0 is used to indicate unknown frame size.
137        sizes[n] = 0;
138      }
139#endif
140      n++;
141    }
142    sp = next_sp;
143  }
144  return n;
145}
146