1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/debug/stack_trace.h"
6
7#include <string.h>
8
9#include <algorithm>
10#include <sstream>
11
12#include "base/macros.h"
13
14#if HAVE_TRACE_STACK_FRAME_POINTERS && defined(OS_ANDROID)
15#include <pthread.h>
16#include "base/process/process_handle.h"
17#include "base/threading/platform_thread.h"
18#endif
19
20namespace base {
21namespace debug {
22
23StackTrace::StackTrace(const void* const* trace, size_t count) {
24  count = std::min(count, arraysize(trace_));
25  if (count)
26    memcpy(trace_, trace, count * sizeof(trace_[0]));
27  count_ = count;
28}
29
30StackTrace::~StackTrace() {
31}
32
33const void *const *StackTrace::Addresses(size_t* count) const {
34  *count = count_;
35  if (count_)
36    return trace_;
37  return NULL;
38}
39
40std::string StackTrace::ToString() const {
41  std::stringstream stream;
42#if !defined(__UCLIBC__)
43  OutputToStream(&stream);
44#endif
45  return stream.str();
46}
47
48#if HAVE_TRACE_STACK_FRAME_POINTERS
49
50#if defined(OS_ANDROID)
51
52static uintptr_t GetStackEnd() {
53  // Bionic reads proc/maps on every call to pthread_getattr_np() when called
54  // from the main thread. So we need to cache end of stack in that case to get
55  // acceptable performance.
56  // For all other threads pthread_getattr_np() is fast enough as it just reads
57  // values from its pthread_t argument.
58  static uintptr_t main_stack_end = 0;
59
60  bool is_main_thread = GetCurrentProcId() == PlatformThread::CurrentId();
61
62  if (is_main_thread && main_stack_end) {
63    return main_stack_end;
64  }
65
66  uintptr_t stack_begin = 0;
67  size_t stack_size = 0;
68  pthread_attr_t attributes;
69  int error = pthread_getattr_np(pthread_self(), &attributes);
70  if (!error) {
71    error = pthread_attr_getstack(
72        &attributes,
73        reinterpret_cast<void**>(&stack_begin),
74        &stack_size);
75    pthread_attr_destroy(&attributes);
76  }
77  DCHECK(!error);
78
79  uintptr_t stack_end = stack_begin + stack_size;
80  if (is_main_thread) {
81    main_stack_end = stack_end;
82  }
83  return stack_end;
84}
85
86#endif  // defined(OS_ANDROID)
87
88size_t TraceStackFramePointers(const void** out_trace,
89                               size_t max_depth,
90                               size_t skip_initial) {
91  // Usage of __builtin_frame_address() enables frame pointers in this
92  // function even if they are not enabled globally. So 'sp' will always
93  // be valid.
94  uintptr_t sp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
95
96#if defined(OS_ANDROID)
97  uintptr_t stack_end = GetStackEnd();
98#endif
99
100  size_t depth = 0;
101  while (depth < max_depth) {
102#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__)
103    // GCC and LLVM generate slightly different frames on ARM, see
104    // https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates
105    // x86-compatible frame, while GCC needs adjustment.
106    sp -= sizeof(uintptr_t);
107#endif
108
109#if defined(OS_ANDROID)
110    // Both sp[0] and s[1] must be valid.
111    if (sp + 2 * sizeof(uintptr_t) > stack_end) {
112      break;
113    }
114#endif
115
116    if (skip_initial != 0) {
117      skip_initial--;
118    } else {
119      out_trace[depth++] = reinterpret_cast<const void**>(sp)[1];
120    }
121
122    // Find out next frame pointer
123    // (heuristics are from TCMalloc's stacktrace functions)
124    {
125      uintptr_t next_sp = reinterpret_cast<const uintptr_t*>(sp)[0];
126
127      // With the stack growing downwards, older stack frame must be
128      // at a greater address that the current one.
129      if (next_sp <= sp) break;
130
131      // Assume stack frames larger than 100,000 bytes are bogus.
132      if (next_sp - sp > 100000) break;
133
134      // Check alignment.
135      if (sp & (sizeof(void*) - 1)) break;
136
137      sp = next_sp;
138    }
139  }
140
141  return depth;
142}
143
144#endif  // HAVE_TRACE_STACK_FRAME_POINTERS
145
146}  // namespace debug
147}  // namespace base
148