Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -27,6 +27,7 @@ sanitizer_symbolizer.cc sanitizer_symbolizer_libbacktrace.cc sanitizer_symbolizer_win.cc + sanitizer_symbolizer_mac.cc sanitizer_tls_get_addr.cc sanitizer_thread_registry.cc sanitizer_win.cc) Index: lib/sanitizer_common/sanitizer_symbolizer.h =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer.h +++ lib/sanitizer_common/sanitizer_symbolizer.h @@ -137,6 +137,21 @@ }; }; +class ExternalSymbolizerInterface { + public: + // Can't declare pure virtual functions in sanitizer runtimes: + // __cxa_pure_virtual might be unavailable. + virtual char *SendCommand(uptr addr, bool is_data, const char *module_name, + uptr module_offset) { + UNIMPLEMENTED(); + } + + virtual void ParseSymbolizedStackFrames(const char *str, + SymbolizedStack *res) { + UNIMPLEMENTED(); + } +}; + } // namespace __sanitizer #endif // SANITIZER_SYMBOLIZER_H Index: lib/sanitizer_common/sanitizer_symbolizer_mac.h =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_mac.h +++ lib/sanitizer_common/sanitizer_symbolizer_mac.h @@ -0,0 +1,53 @@ +//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Header for Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_SYMBOLIZER_MAC_H +#define SANITIZER_SYMBOLIZER_MAC_H + +#include "sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_symbolizer.h" + +#include +#include +#include + +namespace __sanitizer { + +class AtosSymbolizer : public ExternalSymbolizerInterface { + public: + explicit AtosSymbolizer(const char *atos_path) : atos_path_(atos_path) {} + + void ParseSymbolizedStackFrames(const char *str, SymbolizedStack *res); + char *SendCommand(uptr addr, bool is_data, const char *module_name, + uptr module_offset); + + private: + bool StartSubprocessIfNotStarted(); + + const char *atos_path_; + int fd_to_child_; + + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_MAC + +#endif // SANITIZER_SYMBOLIZER_MAC_H Index: lib/sanitizer_common/sanitizer_symbolizer_mac.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_mac.cc +++ lib/sanitizer_common/sanitizer_symbolizer_mac.cc @@ -0,0 +1,118 @@ +//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Implementation of Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_mac.h" + +namespace __sanitizer { + +bool AtosSymbolizer::StartSubprocessIfNotStarted() { + if (fd_to_child_) + return true; + + int main_process_pid = getpid(); + int fd; + + // Use forkpty to disable buffering in the new terminal. + int pid = forkpty(&fd, 0, 0, 0); + if (pid == -1) { + // Fork() failed. + Report("WARNING: failed to fork external symbolizer (errno: %d)\n", errno); + return false; + } else if (pid == 0) { + // Child subprocess. + char pid_str[16] = {0}; + internal_snprintf(pid_str, sizeof(pid_str), "%d", main_process_pid); + execl(atos_path_, atos_path_, "-p", pid_str, (char *)0); + internal__exit(1); + } + + // Continue execution in parent process. + fd_to_child_ = fd; + + // Disable echo in the new terminal. + struct termios termflags; + tcgetattr(fd_to_child_, &termflags); + termflags.c_lflag &= ~ECHO; + tcsetattr(fd_to_child_, TCSANOW, &termflags); + + return true; +} + +static const char *ExtractToken(const char *str, const char *delimiter, + char **result) { + const char *found_delimiter = internal_strstr(str, delimiter); + uptr prefix_len = + found_delimiter ? found_delimiter - str : internal_strlen(str); + *result = (char *)InternalAlloc(prefix_len + 1); + internal_memcpy(*result, str, prefix_len); + (*result)[prefix_len] = '\0'; + const char *prefix_end = str + prefix_len; + if (*prefix_end != '\0') + prefix_end += internal_strlen(delimiter); + return prefix_end; +} + +void AtosSymbolizer::ParseSymbolizedStackFrames(const char *str, + SymbolizedStack *res) { + const char *newstr = str; + newstr = ExtractToken(newstr, " (in", &res->info.function); + + char *extracted_module_name; + newstr = ExtractToken(newstr, ") ", &extracted_module_name); + InternalFree(extracted_module_name); + + if (newstr[0] == '(') { + newstr++; + newstr = ExtractToken(newstr, ":", &res->info.file); + char *extracted_line_number; + newstr = ExtractToken(newstr, ")", &extracted_line_number); + res->info.line = internal_atoll(extracted_line_number); + InternalFree(extracted_line_number); + } +} + +char *AtosSymbolizer::SendCommand(uptr addr, bool is_data, + const char *module_name, uptr module_offset) { + StartSubprocessIfNotStarted(); + + uptr length = internal_snprintf(buffer_, kBufferSize, "0x%zx\n", addr); + uptr write_len = internal_write(fd_to_child_, buffer_, length); + if (write_len == 0 || write_len == (uptr)-1) { + Report("WARNING: Can't write to symbolizer at fd %d\n", fd_to_child_); + return nullptr; + } + + length = 0; + while (true) { + uptr just_read = + internal_read(fd_to_child_, buffer_ + length, kBufferSize - length - 1); + if (just_read == 0 || just_read == (uptr)-1) { + Report("WARNING: Can't read from symbolizer at fd %d\n", fd_to_child_); + return nullptr; + } + length += just_read; + if (length >= 1 && buffer_[length - 1] == '\n') + break; + } + buffer_[length] = '\0'; + return buffer_; +} + +} // namespace __sanitizer + +#endif // SANITIZER_MAC Index: lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -23,6 +23,7 @@ #include "sanitizer_procmaps.h" #include "sanitizer_symbolizer.h" #include "sanitizer_symbolizer_libbacktrace.h" +#include "sanitizer_symbolizer_mac.h" #include #include @@ -94,15 +95,57 @@ return ret; } -class ExternalSymbolizerInterface { - public: - // Can't declare pure virtual functions in sanitizer runtimes: - // __cxa_pure_virtual might be unavailable. - virtual char *SendCommand(bool is_data, const char *module_name, - uptr module_offset) { - UNIMPLEMENTED(); +static void POSIXParseSymbolizedStackFrames(const char *str, + SymbolizedStack *res) { + uptr orig_addr = res->info.address; + const char *orig_module_name = res->info.module; + uptr orig_module_offset = res->info.module_offset; + + bool top_frame = true; + SymbolizedStack *last = res; + while (true) { + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + break; + } + SymbolizedStack *cur; + if (top_frame) { + cur = res; + top_frame = false; + } else { + cur = SymbolizedStack::New(orig_addr); + cur->info.FillAddressAndModuleInfo(orig_addr, orig_module_name, + orig_module_offset); + last->next = cur; + last = cur; + } + + AddressInfo *info = &cur->info; + info->function = function_name; + // Parse :: buffer. + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + const char *line_info = ExtractToken(file_line_info, ":", &info->file); + line_info = ExtractInt(line_info, ":", &info->line); + line_info = ExtractInt(line_info, "", &info->column); + InternalFree(file_line_info); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; + } + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; + } } -}; +} // SymbolizerProcess encapsulates communication between the tool and // external symbolizer program, running in a different subprocess. @@ -120,7 +163,12 @@ CHECK_NE(path_[0], '\0'); } - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + void ParseSymbolizedStackFrames(const char *str, SymbolizedStack *res) { + POSIXParseSymbolizedStackFrames(str, res); + } + + char *SendCommand(uptr addr, bool is_data, const char *module_name, + uptr module_offset) { for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { // Start or restart symbolizer if we failed to send command to it. if (char *res = SendCommandImpl(is_data, module_name, module_offset)) @@ -400,7 +448,12 @@ : addr2line_path_(addr2line_path), allocator_(allocator), addr2line_pool_(16) {} - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + void ParseSymbolizedStackFrames(const char *str, SymbolizedStack *res) { + POSIXParseSymbolizedStackFrames(str, res); + } + + char *SendCommand(uptr addr, bool is_data, const char *module_name, + uptr module_offset) { if (is_data) return 0; Addr2LineProcess *addr2line = 0; @@ -416,7 +469,7 @@ new(*allocator_) Addr2LineProcess(addr2line_path_, module_name); addr2line_pool_.push_back(addr2line); } - return addr2line->SendCommand(is_data, module_name, module_offset); + return addr2line->SendCommand(addr, is_data, module_name, module_offset); } private: @@ -452,7 +505,12 @@ return 0; } - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + void ParseSymbolizedStackFrames(const char *str, SymbolizedStack *res) { + POSIXParseSymbolizedStackFrames(str, res); + } + + char *SendCommand(uptr addr, bool is_data, const char *module_name, + uptr module_offset) { SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data : __sanitizer_symbolize_code; if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) @@ -495,7 +553,9 @@ class InternalSymbolizer { public: static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; } - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + void ParseSymbolizedStackFrames(const char *str, SymbolizedStack *res) { } + char *SendCommand(uptr addr, bool is_data, const char *module_name, + uptr module_offset) { return 0; } void Flush() { } @@ -531,55 +591,14 @@ SymbolizedStack *res = SymbolizedStack::New(addr); res->info.FillAddressAndModuleInfo(addr, module_name, module_offset); - const char *str = SendCommand(false, module_name, module_offset); + const char *str = SendCommand(addr, false, module_name, module_offset); if (str == 0) { // Symbolizer was not initialized or failed. return res; } - bool top_frame = true; - SymbolizedStack *last = res; - while (true) { - char *function_name = 0; - str = ExtractToken(str, "\n", &function_name); - CHECK(function_name); - if (function_name[0] == '\0') { - // There are no more frames. - break; - } - SymbolizedStack *cur; - if (top_frame) { - cur = res; - top_frame = false; - } else { - cur = SymbolizedStack::New(addr); - cur->info.FillAddressAndModuleInfo(addr, module_name, module_offset); - last->next = cur; - last = cur; - } + ParseSymbolizedStackFrames(str, res); - AddressInfo *info = &cur->info; - info->function = function_name; - // Parse :: buffer. - char *file_line_info = 0; - str = ExtractToken(str, "\n", &file_line_info); - CHECK(file_line_info); - const char *line_info = ExtractToken(file_line_info, ":", &info->file); - line_info = ExtractInt(line_info, ":", &info->line); - line_info = ExtractInt(line_info, "", &info->column); - InternalFree(file_line_info); - - // Functions and filenames can be "??", in which case we write 0 - // to address info to mark that names are unknown. - if (0 == internal_strcmp(info->function, "??")) { - InternalFree(info->function); - info->function = 0; - } - if (0 == internal_strcmp(info->file, "??")) { - InternalFree(info->file); - info->file = 0; - } - } return res; } @@ -599,7 +618,7 @@ if (libbacktrace_symbolizer_->SymbolizeData(info)) return true; } - const char *str = SendCommand(true, module_name, module_offset); + const char *str = SendCommand(addr, true, module_name, module_offset); if (str == 0) return true; str = ExtractToken(str, "\n", &info->name); @@ -652,23 +671,40 @@ } private: - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + char *SendCommand(uptr addr, bool is_data, const char *module_name, + uptr module_offset) { mu_.CheckLocked(); // First, try to use internal symbolizer. if (internal_symbolizer_) { SymbolizerScope sym_scope(this); - return internal_symbolizer_->SendCommand(is_data, module_name, + return internal_symbolizer_->SendCommand(addr, is_data, module_name, module_offset); } // Otherwise, fall back to external symbolizer. if (external_symbolizer_) { SymbolizerScope sym_scope(this); - return external_symbolizer_->SendCommand(is_data, module_name, + return external_symbolizer_->SendCommand(addr, is_data, module_name, module_offset); } return 0; } + void ParseSymbolizedStackFrames(const char *str, SymbolizedStack *res) { + mu_.CheckLocked(); + // First, try to use internal symbolizer. + if (internal_symbolizer_) { + SymbolizerScope sym_scope(this); + internal_symbolizer_->ParseSymbolizedStackFrames(str, res); + return; + } + // Otherwise, fall back to external symbolizer. + if (external_symbolizer_) { + SymbolizerScope sym_scope(this); + external_symbolizer_->ParseSymbolizedStackFrames(str, res); + return; + } + } + LoadedModule *FindModuleForAddress(uptr address) { mu_.CheckLocked(); bool modules_were_reloaded = false; @@ -752,6 +788,11 @@ external_symbolizer = new(symbolizer_allocator_) Addr2LinePool(addr2line_path, &symbolizer_allocator_); } +#if SANITIZER_MAC + } else if (const char *atos_path = FindPathToBinary("atos")) { + external_symbolizer = + new(symbolizer_allocator_) AtosSymbolizer(atos_path); +#endif // SANITIZER_MAC } } }