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

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

Issue 11362048: GTTF: Make Linux stack dump signal handler async-signal safe. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixes Created 8 years, 1 month 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 | Annotate | Revision Log
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 <errno.h> 7 #include <errno.h>
8 #include <execinfo.h> 8 #include <execinfo.h>
9 #include <fcntl.h> 9 #include <fcntl.h>
10 #include <signal.h> 10 #include <signal.h>
11 #include <stdio.h> 11 #include <stdio.h>
12 #include <stdlib.h> 12 #include <stdlib.h>
13 #include <sys/param.h> 13 #include <sys/param.h>
14 #include <sys/stat.h> 14 #include <sys/stat.h>
15 #include <sys/types.h> 15 #include <sys/types.h>
16 #include <unistd.h> 16 #include <unistd.h>
17 17
18 #include <string> 18 #include <ostream>
19 #include <vector>
20
21 #if defined(__GLIBCXX__)
22 #include <cxxabi.h>
23 #endif
24
25 #if defined(OS_MACOSX)
26 #include <AvailabilityMacros.h>
27 #endif
28 19
29 #include "base/basictypes.h" 20 #include "base/basictypes.h"
21 #include "base/debug/debugger.h"
30 #include "base/eintr_wrapper.h" 22 #include "base/eintr_wrapper.h"
31 #include "base/logging.h" 23 #include "base/logging.h"
32 #include "base/memory/scoped_ptr.h" 24 #include "base/string_number_conversions.h"
33 #include "base/safe_strerror_posix.h"
34 #include "base/string_piece.h"
35 #include "base/stringprintf.h"
36 25
37 #if defined(USE_SYMBOLIZE) 26 #if defined(USE_SYMBOLIZE)
38 #include "base/third_party/symbolize/symbolize.h" 27 #include "base/third_party/symbolize/symbolize.h"
39 #endif 28 #endif
40 29
41 namespace base { 30 namespace base {
42 namespace debug { 31 namespace debug {
43 32
44 namespace { 33 namespace {
45 34
46 // The prefix used for mangled symbols, per the Itanium C++ ABI: 35 class BacktraceOutputHandler {
47 // http://www.codesourcery.com/cxx-abi/abi.html#mangling 36 public:
48 const char kMangledSymbolPrefix[] = "_Z"; 37 virtual void HandleOutput(const char* output) = 0;
49 38
50 // Characters that can be used for symbols, generated by Ruby: 39 protected:
51 // (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join 40 virtual ~BacktraceOutputHandler() {}
52 const char kSymbolCharacters[] = 41 };
53 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
54 42
55 #if !defined(USE_SYMBOLIZE) 43 void ProcessBacktrace(void *const *trace,
56 // Demangles C++ symbols in the given text. Example: 44 int size,
57 // 45 BacktraceOutputHandler* handler) {
58 // "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]" 46 // NOTE: This code MUST be async-signal safe (it's used by in-process
59 // => 47 // stack dumping signal handler). NO malloc or stdio is allowed here.
60 // "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
61 void DemangleSymbols(std::string* text) {
62 #if defined(__GLIBCXX__)
63 48
64 std::string::size_type search_from = 0; 49 for (int i = 0; i < size; ++i) {
65 while (search_from < text->size()) { 50 handler->HandleOutput("\t");
66 // Look for the start of a mangled symbol, from search_from.
67 std::string::size_type mangled_start =
68 text->find(kMangledSymbolPrefix, search_from);
69 if (mangled_start == std::string::npos) {
70 break; // Mangled symbol not found.
71 }
72 51
73 // Look for the end of the mangled symbol. 52 char buf[1024] = { '\0' };
74 std::string::size_type mangled_end =
75 text->find_first_not_of(kSymbolCharacters, mangled_start);
76 if (mangled_end == std::string::npos) {
77 mangled_end = text->size();
78 }
79 std::string mangled_symbol =
80 text->substr(mangled_start, mangled_end - mangled_start);
81
82 // Try to demangle the mangled symbol candidate.
83 int status = 0;
84 scoped_ptr_malloc<char> demangled_symbol(
85 abi::__cxa_demangle(mangled_symbol.c_str(), NULL, 0, &status));
86 if (status == 0) { // Demangling is successful.
87 // Remove the mangled symbol.
88 text->erase(mangled_start, mangled_end - mangled_start);
89 // Insert the demangled symbol.
90 text->insert(mangled_start, demangled_symbol.get());
91 // Next time, we'll start right after the demangled symbol we inserted.
92 search_from = mangled_start + strlen(demangled_symbol.get());
93 } else {
94 // Failed to demangle. Retry after the "_Z" we just found.
95 search_from = mangled_start + 2;
96 }
97 }
98
99 #endif // defined(__GLIBCXX__)
100 }
101 #endif // !defined(USE_SYMBOLIZE)
102
103 // Gets the backtrace as a vector of strings. If possible, resolve symbol
104 // names and attach these. Otherwise just use raw addresses. Returns true
105 // if any symbol name is resolved. Returns false on error and *may* fill
106 // in |error_message| if an error message is available.
107 bool GetBacktraceStrings(void *const *trace, int size,
108 std::vector<std::string>* trace_strings,
109 std::string* error_message) {
110 bool symbolized = false;
111 53
112 #if defined(USE_SYMBOLIZE) 54 #if defined(USE_SYMBOLIZE)
113 for (int i = 0; i < size; ++i) {
114 char symbol[1024];
115 // Subtract by one as return address of function may be in the next 55 // Subtract by one as return address of function may be in the next
116 // function when a function is annotated as noreturn. 56 // function when a function is annotated as noreturn.
117 if (google::Symbolize(static_cast<char *>(trace[i]) - 1, 57 void* address = static_cast<char*>(trace[i]) - 1;
118 symbol, sizeof(symbol))) { 58 if (google::Symbolize(address, buf, sizeof(buf)))
119 // Don't call DemangleSymbols() here as the symbol is demangled by 59 handler->HandleOutput(buf);
120 // google::Symbolize(). 60 else
121 trace_strings->push_back( 61 handler->HandleOutput("<unknown>");
122 base::StringPrintf("%s [%p]", symbol, trace[i])); 62
123 symbolized = true; 63 handler->HandleOutput(" ");
124 } else {
125 trace_strings->push_back(base::StringPrintf("%p", trace[i]));
126 }
127 }
128 #else
129 scoped_ptr_malloc<char*> trace_symbols(backtrace_symbols(trace, size));
130 if (trace_symbols.get()) {
131 for (int i = 0; i < size; ++i) {
132 std::string trace_symbol = trace_symbols.get()[i];
133 DemangleSymbols(&trace_symbol);
134 trace_strings->push_back(trace_symbol);
135 }
136 symbolized = true;
137 } else {
138 if (error_message)
139 *error_message = safe_strerror(errno);
140 for (int i = 0; i < size; ++i) {
141 trace_strings->push_back(base::StringPrintf("%p", trace[i]));
142 }
143 }
144 #endif // defined(USE_SYMBOLIZE) 64 #endif // defined(USE_SYMBOLIZE)
145 65
146 return symbolized; 66 handler->HandleOutput("[0x");
67 internal::itoa_r(reinterpret_cast<intptr_t>(trace[i]),
68 buf, sizeof(buf), 16);
69 handler->HandleOutput(buf);
70 handler->HandleOutput("]\n");
71 }
147 } 72 }
148 73
149 void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) { 74 void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) {
75 // NOTE: This code MUST be async-signal safe.
76 // NO malloc or stdio is allowed here.
77
150 if (BeingDebugged()) 78 if (BeingDebugged())
151 BreakDebugger(); 79 BreakDebugger();
152 80
153 #if defined(OS_MACOSX) 81 char buf[1024] = { '\0' };
154 // TODO(phajdan.jr): Fix async-signal non-safety (http://crbug.com/101155). 82 strncat(buf, "Received signal ", sizeof(buf) - 1);
155 DLOG(ERROR) << "Received signal " << signal; 83 internal::itoa_r(signal, buf + strlen(buf), sizeof(buf) - strlen(buf), 10);
156 StackTrace().PrintBacktrace(); 84 RAW_LOG(ERROR, buf);
157 #endif 85
86 debug::StackTrace().PrintBacktrace();
158 87
159 // TODO(shess): Port to Linux. 88 // TODO(shess): Port to Linux.
160 #if defined(OS_MACOSX) 89 #if defined(OS_MACOSX)
161 // TODO(shess): Port to 64-bit. 90 // TODO(shess): Port to 64-bit.
162 #if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS 91 #if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS
163 char buf[1024];
164 size_t len; 92 size_t len;
165 93
166 // NOTE: Even |snprintf()| is not on the approved list for signal 94 // NOTE: Even |snprintf()| is not on the approved list for signal
167 // handlers, but buffered I/O is definitely not on the list due to 95 // handlers, but buffered I/O is definitely not on the list due to
168 // potential for |malloc()|. 96 // potential for |malloc()|.
169 len = static_cast<size_t>( 97 len = static_cast<size_t>(
170 snprintf(buf, sizeof(buf), 98 snprintf(buf, sizeof(buf),
171 "ax: %x, bx: %x, cx: %x, dx: %x\n", 99 "ax: %x, bx: %x, cx: %x, dx: %x\n",
172 context->uc_mcontext->__ss.__eax, 100 context->uc_mcontext->__ss.__eax,
173 context->uc_mcontext->__ss.__ebx, 101 context->uc_mcontext->__ss.__ebx,
(...skipping 20 matching lines...) Expand all
194 context->uc_mcontext->__ss.__ds, 122 context->uc_mcontext->__ss.__ds,
195 context->uc_mcontext->__ss.__es, 123 context->uc_mcontext->__ss.__es,
196 context->uc_mcontext->__ss.__fs, 124 context->uc_mcontext->__ss.__fs,
197 context->uc_mcontext->__ss.__gs)); 125 context->uc_mcontext->__ss.__gs));
198 write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1)); 126 write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1));
199 #endif // ARCH_CPU_32_BITS 127 #endif // ARCH_CPU_32_BITS
200 #endif // defined(OS_MACOSX) 128 #endif // defined(OS_MACOSX)
201 _exit(1); 129 _exit(1);
202 } 130 }
203 131
132 class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
133 public:
134 PrintBacktraceOutputHandler() {}
135
136 virtual void HandleOutput(const char* output) {
137 // NOTE: This code MUST be async-signal safe (it's used by in-process
138 // stack dumping signal handler). NO malloc or stdio is allowed here.
139 HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output)));
140 }
141
142 private:
143 DISALLOW_COPY_AND_ASSIGN(PrintBacktraceOutputHandler);
144 };
145
146 class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
147 public:
148 StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {
149 }
150
151 virtual void HandleOutput(const char* output) {
152 (*os_) << output;
153 }
154
155 private:
156 std::ostream* os_;
157
158 DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler);
159 };
160
161 void WarmUpBacktrace() {
162 // Warm up stack trace infrastructure. It turns out that on the first
163 // call glibc initializes some internal data structures using pthread_once,
164 // and even backtrace() can call malloc(), leading to hangs.
165 //
166 // Example stack trace snippet (with tcmalloc):
167 //
168 // #8 0x0000000000a173b5 in tc_malloc
169 // at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161
170 // #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517
171 // #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262
172 // #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
173 // #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1")
174 // at dl-open.c:639
175 // #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89
176 // #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
177 // #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48
178 // #16 __GI___libc_dlopen_mode at dl-libc.c:165
179 // #17 0x00007ffff61ef8f5 in init
180 // at ../sysdeps/x86_64/../ia64/backtrace.c:53
181 // #18 0x00007ffff6aad400 in pthread_once
182 // at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104
183 // #19 0x00007ffff61efa14 in __GI___backtrace
184 // at ../sysdeps/x86_64/../ia64/backtrace.c:104
185 // #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace
186 // at base/debug/stack_trace_posix.cc:175
187 // #21 0x00000000007a4ae5 in
188 // base::(anonymous namespace)::StackDumpSignalHandler
189 // at base/process_util_posix.cc:172
190 // #22 <signal handler called>
191 StackTrace stack_trace;
192 }
193
204 } // namespace 194 } // namespace
205 195
206 #if !defined(OS_IOS) 196 #if !defined(OS_IOS)
207 bool EnableInProcessStackDumping() { 197 bool EnableInProcessStackDumping() {
208 // When running in an application, our code typically expects SIGPIPE 198 // When running in an application, our code typically expects SIGPIPE
209 // to be ignored. Therefore, when testing that same code, it should run 199 // to be ignored. Therefore, when testing that same code, it should run
210 // with SIGPIPE ignored as well. 200 // with SIGPIPE ignored as well.
211 struct sigaction action; 201 struct sigaction action;
212 memset(&action, 0, sizeof(action)); 202 memset(&action, 0, sizeof(action));
213 action.sa_handler = SIG_IGN; 203 action.sa_handler = SIG_IGN;
214 sigemptyset(&action.sa_mask); 204 sigemptyset(&action.sa_mask);
215 bool success = (sigaction(SIGPIPE, &action, NULL) == 0); 205 bool success = (sigaction(SIGPIPE, &action, NULL) == 0);
216 206
207 // Avoid hangs during backtrace initialization, see above.
208 WarmUpBacktrace();
209
217 sig_t handler = reinterpret_cast<sig_t>(&StackDumpSignalHandler); 210 sig_t handler = reinterpret_cast<sig_t>(&StackDumpSignalHandler);
218 success &= (signal(SIGILL, handler) != SIG_ERR); 211 success &= (signal(SIGILL, handler) != SIG_ERR);
219 success &= (signal(SIGABRT, handler) != SIG_ERR); 212 success &= (signal(SIGABRT, handler) != SIG_ERR);
220 success &= (signal(SIGFPE, handler) != SIG_ERR); 213 success &= (signal(SIGFPE, handler) != SIG_ERR);
221 success &= (signal(SIGBUS, handler) != SIG_ERR); 214 success &= (signal(SIGBUS, handler) != SIG_ERR);
222 success &= (signal(SIGSEGV, handler) != SIG_ERR); 215 success &= (signal(SIGSEGV, handler) != SIG_ERR);
223 success &= (signal(SIGSYS, handler) != SIG_ERR); 216 success &= (signal(SIGSYS, handler) != SIG_ERR);
224 217
225 return success; 218 return success;
226 } 219 }
227 #endif // !defined(OS_IOS) 220 #endif // !defined(OS_IOS)
228 221
229 StackTrace::StackTrace() { 222 StackTrace::StackTrace() {
223 // NOTE: This code MUST be async-signal safe (it's used by in-process
224 // stack dumping signal handler). NO malloc or stdio is allowed here.
225
230 // Though the backtrace API man page does not list any possible negative 226 // Though the backtrace API man page does not list any possible negative
231 // return values, we take no chance. 227 // return values, we take no chance.
232 count_ = std::max(backtrace(trace_, arraysize(trace_)), 0); 228 count_ = std::max(backtrace(trace_, arraysize(trace_)), 0);
233 } 229 }
234 230
235 void StackTrace::PrintBacktrace() const { 231 void StackTrace::PrintBacktrace() const {
236 fflush(stderr); 232 // NOTE: This code MUST be async-signal safe (it's used by in-process
237 std::vector<std::string> trace_strings; 233 // stack dumping signal handler). NO malloc or stdio is allowed here.
238 GetBacktraceStrings(trace_, count_, &trace_strings, NULL); 234
239 for (size_t i = 0; i < trace_strings.size(); ++i) { 235 PrintBacktraceOutputHandler handler;
240 fprintf(stderr, "\t%s\n", trace_strings[i].c_str()); 236 ProcessBacktrace(trace_, count_, &handler);
241 }
242 } 237 }
243 238
244 void StackTrace::OutputToStream(std::ostream* os) const { 239 void StackTrace::OutputToStream(std::ostream* os) const {
245 std::vector<std::string> trace_strings; 240 StreamBacktraceOutputHandler handler(os);
246 std::string error_message; 241 ProcessBacktrace(trace_, count_, &handler);
247 if (GetBacktraceStrings(trace_, count_, &trace_strings, &error_message)) { 242 }
248 (*os) << "Backtrace:\n"; 243
249 } else { 244 namespace internal {
Scott Hess - ex-Googler 2012/11/07 01:02:26 itoa() is defined as handling bases between 2 and
250 if (!error_message.empty()) 245
251 error_message = " (" + error_message + ")"; 246 // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
252 (*os) << "Unable to get symbols for backtrace" << error_message << ". " 247 char *itoa_r(intptr_t i, char *buf, size_t sz, int base) {
Scott Hess - ex-Googler 2012/11/07 01:02:26 You might consider just requiring sz to be able to
253 << "Dumping raw addresses in trace:\n"; 248 // Make sure we can write at least one NUL byte.
249 size_t n = 1;
250 if (n > sz)
251 return NULL;
252
253 char *start = buf;
254
255 // Handle negative numbers.
256 if (i < 0) {
jar (doing other things) 2012/11/07 00:32:07 This should only be handled if base is 10.
Scott Hess - ex-Googler 2012/11/07 01:02:26 As currently used, you could just push the negativ
257 // Make sure we can write the '-' character.
258 if (++n > sz) {
259 *start = '\000';
260 return NULL;
261 }
262 *start++ = '-';
254 } 263 }
255 264
256 for (size_t i = 0; i < trace_strings.size(); ++i) { 265 uintptr_t j = (i > 0) ? i : -i;
jar (doing other things) 2012/11/07 00:32:07 The fixup for signed conversion should only be don
257 (*os) << "\t" << trace_strings[i] << "\n"; 266
267 // Loop until we have converted the entire number. Output at least one
268 // character (i.e. '0').
269 char *ptr = start;
270 do {
271 // Make sure there is still enough space left in our output buffer.
272 if (++n > sz) {
273 buf = NULL;
Scott Hess - ex-Googler 2012/11/07 01:02:26 I think this would make more sense as return NULL,
274 break;
Scott Hess - ex-Googler 2012/11/07 01:02:26 I think this should just return NULL, or *buf='\00
275 }
276
277 // Output the next digit.
278 *ptr++ = "0123456789abcdef"[j % base];
279 j /= base;
280 } while (j);
281
282 // Terminate the output with a NUL character.
283 *ptr = '\000';
284
285 // Conversion to ASCII actually resulted in the digits being in reverse
286 // order. We can't easily generate them in forward order, as we can't tell
287 // the number of characters needed until we are done converting.
288 // So, now, we reverse the string (except for the possible "-" sign).
289 while (--ptr > start) {
290 char ch = *ptr;
291 *ptr = *start;
292 *start++ = ch;
258 } 293 }
294 return buf;
259 } 295 }
260 296
297 } // namespace internal
298
261 } // namespace debug 299 } // namespace debug
262 } // namespace base 300 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698