Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(847)

Side by Side Diff: base/debug/stack_trace.cc

Issue 1975393002: Check stack pointer to be inside stack when unwinding. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Implement mincore() approach Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/debug/stack_trace.h" 5 #include "base/debug/stack_trace.h"
6 6
7 #include <string.h> 7 #include <string.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <sstream> 10 #include <sstream>
11 11
12 #include "base/macros.h" 12 #include "base/macros.h"
13 13
14 #if HAVE_TRACE_STACK_FRAME_POINTERS && \
15 (defined(OS_ANDROID) || defined(OS_LINUX))
16 #define HAVE_MINCORE
17 #include <sys/mman.h>
18 #include <unistd.h>
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/process/process_metrics.h"
21 #endif
22
23
14 namespace base { 24 namespace base {
15 namespace debug { 25 namespace debug {
16 26
17 StackTrace::StackTrace(const void* const* trace, size_t count) { 27 StackTrace::StackTrace(const void* const* trace, size_t count) {
18 count = std::min(count, arraysize(trace_)); 28 count = std::min(count, arraysize(trace_));
19 if (count) 29 if (count)
20 memcpy(trace_, trace, count * sizeof(trace_[0])); 30 memcpy(trace_, trace, count * sizeof(trace_[0]));
21 count_ = count; 31 count_ = count;
22 } 32 }
23 33
(...skipping 10 matching lines...) Expand all
34 std::string StackTrace::ToString() const { 44 std::string StackTrace::ToString() const {
35 std::stringstream stream; 45 std::stringstream stream;
36 #if !defined(__UCLIBC__) 46 #if !defined(__UCLIBC__)
37 OutputToStream(&stream); 47 OutputToStream(&stream);
38 #endif 48 #endif
39 return stream.str(); 49 return stream.str();
40 } 50 }
41 51
42 #if HAVE_TRACE_STACK_FRAME_POINTERS 52 #if HAVE_TRACE_STACK_FRAME_POINTERS
43 53
54 #ifdef HAVE_MINCORE
55
56 // Attempts to advance |address| by PageCount pages, checking that all
57 // pages are mapped.
58 // Returns true if no unmapped pages were found, and adds PageCount
59 // pages to |address| and rounds result down to a page.
60 // Returns false if unmapped pages were detected, and sets |address| to
61 // the address of the lowest unmapped page.
62 template <size_t PageCount>
Primiano Tucci (use gerrit) 2016/05/31 16:13:06 why do you need a template function with a lambda
63 bool AdvanceMappedPages(uintptr_t* address) {
64 auto probe = [](uintptr_t address, size_t size) -> bool {
65 uint8_t vec[PageCount];
66 int result = HANDLE_EINTR(
Primiano Tucci (use gerrit) 2016/05/31 16:13:07 accordin to its manpage mincore doesn't EINTR (vm
Dmitry Skiba 2016/05/31 21:52:18 Done.
67 mincore(reinterpret_cast<void*>(address), size, vec));
68 if (result == 0) {
69 return true;
70 }
71 // mincore() returns ENOMEM if address range contains unmapped pages.
72 CHECK_EQ(errno, ENOMEM);
Primiano Tucci (use gerrit) 2016/05/31 16:13:06 I'd remove this CHECK or make it a DCHECK. 1) you
Dmitry Skiba 2016/05/31 21:52:18 Done.
73 return false;
74 };
75
76 size_t page_size = GetPageSize();
77
78 // Rewind address to the page boundary.
79 *address -= *address % page_size;
80
81 // Probe the whole range first.
82 if (probe(*address, PageCount * page_size)) {
83 *address += PageCount * page_size;
84 return true;
85 }
86
87 // Binary search leftmost page that is not mapped.
88 for (size_t pages = PageCount; pages != 1;) {
Primiano Tucci (use gerrit) 2016/05/31 16:13:06 I think this is a bit hard to follow as you do a b
Dmitry Skiba 2016/05/31 21:52:18 I don't see how it can be a single loop. When bina
89 size_t half_pages = pages / 2;
90 if (!probe(*address, half_pages * page_size)) {
91 pages = half_pages;
92 continue;
93 }
94 *address += half_pages * page_size;
95 pages -= half_pages;
96 }
97 return false;
98 }
99
100 #endif // HAVE_MINCORE
101
102 PerThreadStackInfo::PerThreadStackInfo()
103 : start_address(0), start_address_final(false) {}
104
44 size_t TraceStackFramePointers(const void** out_trace, 105 size_t TraceStackFramePointers(const void** out_trace,
45 size_t max_depth, 106 size_t max_depth,
46 size_t skip_initial) { 107 size_t skip_initial,
108 PerThreadStackInfo* stack_info) {
47 // Usage of __builtin_frame_address() enables frame pointers in this 109 // Usage of __builtin_frame_address() enables frame pointers in this
48 // function even if they are not enabled globally. So 'sp' will always 110 // function even if they are not enabled globally. So 'sp' will always
49 // be valid. 111 // be valid.
50 uintptr_t sp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)); 112 uintptr_t sp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
51 113
52 size_t depth = 0; 114 size_t depth = 0;
53 while (depth < max_depth) { 115 while (depth < max_depth) {
54 #if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) 116 #if defined(__arm__) && defined(__GNUC__) && !defined(__clang__)
55 // GCC and LLVM generate slightly different frames on ARM, see 117 // GCC and LLVM generate slightly different frames on ARM, see
56 // https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates 118 // https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates
57 // x86-compatible frame, while GCC needs adjustment. 119 // x86-compatible frame, while GCC needs adjustment.
58 sp -= sizeof(uintptr_t); 120 sp -= sizeof(uintptr_t);
59 #endif 121 #endif
60 122
123 #ifdef HAVE_MINCORE
124 if (stack_info) {
125 // Both sp[0] and s[1] must be valid.
126 uintptr_t max_sp = sp + 2 * sizeof(uintptr_t);
127 if (!stack_info->start_address) {
128 // Initialize start_address so that it satisfies while() below.
129 stack_info->start_address = max_sp - 1;
130 }
131 while (max_sp > stack_info->start_address &&
132 !stack_info->start_address_final) {
133 stack_info->start_address_final = !AdvanceMappedPages<32>(
134 &stack_info->start_address);
135 }
136 if (max_sp > stack_info->start_address &&
137 stack_info->start_address_final) {
138 break;
139 }
140 }
141 #endif
142
61 if (skip_initial != 0) { 143 if (skip_initial != 0) {
62 skip_initial--; 144 skip_initial--;
63 } else { 145 } else {
64 out_trace[depth++] = reinterpret_cast<const void**>(sp)[1]; 146 out_trace[depth++] = reinterpret_cast<const void**>(sp)[1];
65 } 147 }
66 148
67 // Find out next frame pointer 149 // Find out next frame pointer
68 // (heuristics are from TCMalloc's stacktrace functions) 150 // (heuristics are from TCMalloc's stacktrace functions)
69 { 151 {
70 uintptr_t next_sp = reinterpret_cast<const uintptr_t*>(sp)[0]; 152 uintptr_t next_sp = reinterpret_cast<const uintptr_t*>(sp)[0];
(...skipping 12 matching lines...) Expand all
83 } 165 }
84 } 166 }
85 167
86 return depth; 168 return depth;
87 } 169 }
88 170
89 #endif // HAVE_TRACE_STACK_FRAME_POINTERS 171 #endif // HAVE_TRACE_STACK_FRAME_POINTERS
90 172
91 } // namespace debug 173 } // namespace debug
92 } // namespace base 174 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698