Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -27,6 +27,7 @@ sanitizer_suppressions.cc sanitizer_symbolizer.cc sanitizer_symbolizer_libbacktrace.cc + sanitizer_symbolizer_mac.cc sanitizer_symbolizer_win.cc sanitizer_tls_get_addr.cc sanitizer_thread_registry.cc Index: lib/sanitizer_common/sanitizer_symbolizer_internal.h =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_internal.h +++ lib/sanitizer_common/sanitizer_symbolizer_internal.h @@ -25,6 +25,8 @@ const char *ExtractToken(const char *str, const char *delims, char **result); const char *ExtractInt(const char *str, const char *delims, int *result); const char *ExtractUptr(const char *str, const char *delims, uptr *result); +const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter, + char **result); class SymbolizerTool { public: @@ -60,12 +62,12 @@ explicit SymbolizerProcess(const char *path); const char *SendCommand(const char *command); - private: + protected: bool Restart(); const char *SendCommandImpl(const char *command); bool ReadFromSymbolizer(char *buffer, uptr max_length); bool WriteToSymbolizer(const char *buffer, uptr length); - bool StartSymbolizerSubprocess(); + virtual bool StartSymbolizerSubprocess(); virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const { UNIMPLEMENTED(); Index: lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc +++ lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -47,6 +47,19 @@ return ret; } +const char *ExtractTokenUpToDelimiter(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; +} + Symbolizer *Symbolizer::GetOrInit() { SpinMutexLock l(&init_mu_); if (symbolizer_) 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,46 @@ +//===-- 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 various sanitizers' runtime libraries. +// +// Header for Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_SYMBOLIZER_MAC_H +#define SANITIZER_SYMBOLIZER_MAC_H + +#include "sanitizer_symbolizer_internal.h" + +namespace __sanitizer { + +class DlAddrSymbolizer : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override; +}; + +class AtosSymbolizerProcess; + +class AtosSymbolizer : public SymbolizerTool { + public: + explicit AtosSymbolizer(const char *path); + + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override; + + private: + AtosSymbolizerProcess *process_; + + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; + +} // namespace __sanitizer + +#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,207 @@ +//===-- 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 various sanitizers' runtime libraries. +// +// Implementation of Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_mac.h" + +namespace __sanitizer { + +#if SANITIZER_MAC + +#include +#include +#include +#include +#include +#include + +bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + Dl_info info; + int result = dladdr((const void *)addr, &info); + if (!result) return false; + stack->info.function = internal_strdup(info.dli_sname); + return true; +} + +bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + return false; +} + +class AtosSymbolizerProcess : public SymbolizerProcess { + public: + explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid) + : SymbolizerProcess(path), + parent_pid_(parent_pid), + fd_to_child_(kInvalidFd), + fork_failed_(false) {} + + void Shutdown() { + fd_to_child_ = input_fd_ = output_fd_ = kInvalidFd; + fork_failed_ = true; + } + + private: + bool StartSymbolizerSubprocess() override { + if (!FileExists(path_)) { + if (!reported_invalid_path_) { + Report("WARNING: invalid path to external symbolizer!\n"); + reported_invalid_path_ = true; + } + return false; + } + + if (fd_to_child_ != kInvalidFd) return true; + if (fork_failed_) return false; + + fd_t fd = kInvalidFd; + // Use forkpty to disable buffering in the new terminal. + int pid = forkpty(&fd, 0, 0, 0); + + if (pid == -1) { + // forkpty() failed. + Report("WARNING: failed to fork external symbolizer (errno: %d)\n", + errno); + fork_failed_ = true; + return false; + } else if (pid == 0) { + // Child subprocess. + ExecuteWithDefaultArgs(path_); + internal__exit(1); + } + + // Continue execution in parent process. + fd_to_child_ = input_fd_ = output_fd_ = fd; + + // Disable echo in the new terminal, disable CR. + struct termios termflags; + tcgetattr(fd_to_child_, &termflags); + termflags.c_oflag &= ~ONLCR; + termflags.c_lflag &= ~ECHO; + tcsetattr(fd_to_child_, TCSANOW, &termflags); + + // Check that symbolizer subprocess started successfully. + int pid_status; + SleepForMillis(kSymbolizerStartupTimeMillis); + int exited_pid = waitpid(pid, &pid_status, WNOHANG); + if (exited_pid != 0) { + // Either waitpid failed, or child has already exited. + Report("WARNING: external symbolizer didn't start up correctly!\n"); + return false; + } + + return true; + } + + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { + return (length >= 1 && buffer_[length - 1] == '\n'); + } + + void ExecuteWithDefaultArgs(const char *path_to_binary) const override { + // The `atos` binary has some issues with DYLD_ROOT_PATH on i386. + unsetenv("DYLD_ROOT_PATH"); + + char pid_str[16] = {0}; + internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_); + execl(path_, path_, "-p", pid_str, (char *)0); + } + + pid_t parent_pid_; + fd_t fd_to_child_; + bool fork_failed_; +}; + +bool ParseCommandOutput(const char *str, SymbolizedStack *res) { + // Trim ending newlines. + char *trim; + ExtractTokenUpToDelimiter(str, "\n", &trim); + + // The line from `atos` is in one of these formats: + // myfunction (in library.dylib) (sourcefile.c:17) + // myfunction (in library.dylib) + 0x1fe + // 0xdeadbeef (in library.dylib) + 0x1fe + // 0xdeadbeef (in library.dylib) + // 0xdeadbeef + + if (internal_strstr(trim, "atos cannot examine process") || + internal_strstr(trim, "unable to get permission to examine process") || + internal_strstr(trim, "An admin user name and password is required") || + internal_strstr(trim, "could not load inserted library") || + internal_strstr(trim, "architecture mismatch between analysis process")) { + Report("atos returned an error: %s\n", trim); + return false; + } + + const char *rest = trim; + char *function_name; + rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name); + if (internal_strncmp(function_name, "0x", 2) != 0) + res->info.function = function_name; + else + InternalFree(function_name); + rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module); + + if (rest[0] == '(') { + rest++; + rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file); + char *extracted_line_number; + rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number); + res->info.line = internal_atoll(extracted_line_number); + InternalFree(extracted_line_number); + } + + InternalFree(trim); + return true; +} + +AtosSymbolizer::AtosSymbolizer(const char *path) + : process_(new AtosSymbolizerProcess(path, getpid())) {} + +bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + internal_snprintf(buffer_, kBufferSize, "0x%zx\n", addr); + const char *buf = process_->SendCommand(buffer_); + if (!buf) return false; + bool result = ParseCommandOutput(buf, stack); + if (!result) { + process_->Shutdown(); + return false; + } + return true; +} + +bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } + +#else // SANITIZER_MAC + +bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + return false; +} + +bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + return false; +} + +AtosSymbolizer::AtosSymbolizer(const char *path) : process_(nullptr) { + UNIMPLEMENTED(); +} + +bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + return false; +} + +bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } + +#endif // SANITIZER_MAC + +} // namespace __sanitizer Index: lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc +++ lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc @@ -46,4 +46,12 @@ EXPECT_STREQ("456;789", rest); } +TEST(Symbolizer, ExtractTokenUpToDelimiter) { + char *token; + const char *rest = ExtractTokenUpToDelimiter("aaa-+-bbb-+-ccc", "-+-", &token); + EXPECT_STREQ("aaa", token); + EXPECT_STREQ("bbb-+-ccc", rest); + InternalFree(token); +} + } // namespace __sanitizer