| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 | // Copyright 2018 The Abseil Authors.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at////      https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.#include "absl/debugging/symbolize.h"#ifndef _WIN32#include <fcntl.h>#include <sys/mman.h>#endif#include <cstring>#include <iostream>#include <memory>#include "gmock/gmock.h"#include "gtest/gtest.h"#include "absl/base/attributes.h"#include "absl/base/casts.h"#include "absl/base/config.h"#include "absl/base/internal/per_thread_tls.h"#include "absl/base/internal/raw_logging.h"#include "absl/base/optimization.h"#include "absl/debugging/internal/stack_consumption.h"#include "absl/memory/memory.h"#include "absl/strings/string_view.h"using testing::Contains;#ifdef _WIN32#define ABSL_SYMBOLIZE_TEST_NOINLINE __declspec(noinline)#else#define ABSL_SYMBOLIZE_TEST_NOINLINE ABSL_ATTRIBUTE_NOINLINE#endif// Functions to symbolize. Use C linkage to avoid mangled names.extern "C" {ABSL_SYMBOLIZE_TEST_NOINLINE void nonstatic_func() {  // The next line makes this a unique function to prevent the compiler from  // folding identical functions together.  volatile int x = __LINE__;  static_cast<void>(x);  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();}ABSL_SYMBOLIZE_TEST_NOINLINE static void static_func() {  // The next line makes this a unique function to prevent the compiler from  // folding identical functions together.  volatile int x = __LINE__;  static_cast<void>(x);  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();}}  // extern "C"struct Foo {  static void func(int x);};// A C++ method that should have a mangled name.ABSL_SYMBOLIZE_TEST_NOINLINE void Foo::func(int) {  // The next line makes this a unique function to prevent the compiler from  // folding identical functions together.  volatile int x = __LINE__;  static_cast<void>(x);  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();}// Create functions that will remain in different text sections in the// final binary when linker option "-z,keep-text-section-prefix" is used.int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.unlikely) unlikely_func() {  return 0;}int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.hot) hot_func() {  return 0;}int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.startup) startup_func() {  return 0;}int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.exit) exit_func() {  return 0;}int /*ABSL_ATTRIBUTE_SECTION_VARIABLE(.text)*/ regular_func() {  return 0;}// Thread-local data may confuse the symbolizer, ensure that it does not.// Variable sizes and order are important.#if ABSL_PER_THREAD_TLSstatic ABSL_PER_THREAD_TLS_KEYWORD char symbolize_test_thread_small[1];static ABSL_PER_THREAD_TLS_KEYWORD char    symbolize_test_thread_big[2 * 1024 * 1024];#endif#if !defined(__EMSCRIPTEN__)// Used below to hopefully inhibit some compiler/linker optimizations// that may remove kHpageTextPadding, kPadding0, and kPadding1 from// the binary.static volatile bool volatile_bool = false;// Force the binary to be large enough that a THP .text remap will succeed.static constexpr size_t kHpageSize = 1 << 21;const char kHpageTextPadding[kHpageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(    .text) = "";#endif  // !defined(__EMSCRIPTEN__)static char try_symbolize_buffer[4096];// A wrapper function for absl::Symbolize() to make the unit test simple.  The// limit must be < sizeof(try_symbolize_buffer).  Returns null if// absl::Symbolize() returns false, otherwise returns try_symbolize_buffer with// the result of absl::Symbolize().static const char *TrySymbolizeWithLimit(void *pc, int limit) {  ABSL_RAW_CHECK(limit <= sizeof(try_symbolize_buffer),                 "try_symbolize_buffer is too small");  // Use the heap to facilitate heap and buffer sanitizer tools.  auto heap_buffer = absl::make_unique<char[]>(sizeof(try_symbolize_buffer));  bool found = absl::Symbolize(pc, heap_buffer.get(), limit);  if (found) {    ABSL_RAW_CHECK(strnlen(heap_buffer.get(), limit) < limit,                   "absl::Symbolize() did not properly terminate the string");    strncpy(try_symbolize_buffer, heap_buffer.get(),            sizeof(try_symbolize_buffer) - 1);    try_symbolize_buffer[sizeof(try_symbolize_buffer) - 1] = '\0';  }  return found ? try_symbolize_buffer : nullptr;}// A wrapper for TrySymbolizeWithLimit(), with a large limit.static const char *TrySymbolize(void *pc) {  return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer));}#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \    defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)TEST(Symbolize, Cached) {  // Compilers should give us pointers to them.  EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));  // The name of an internal linkage symbol is not specified; allow either a  // mangled or an unmangled name here.  const char *static_func_symbol = TrySymbolize((void *)(&static_func));  EXPECT_TRUE(strcmp("static_func", static_func_symbol) == 0 ||              strcmp("static_func()", static_func_symbol) == 0);  EXPECT_TRUE(nullptr == TrySymbolize(nullptr));}TEST(Symbolize, Truncation) {  constexpr char kNonStaticFunc[] = "nonstatic_func";  EXPECT_STREQ("nonstatic_func",               TrySymbolizeWithLimit((void *)(&nonstatic_func),                                     strlen(kNonStaticFunc) + 1));  EXPECT_STREQ("nonstatic_...",               TrySymbolizeWithLimit((void *)(&nonstatic_func),                                     strlen(kNonStaticFunc) + 0));  EXPECT_STREQ("nonstatic...",               TrySymbolizeWithLimit((void *)(&nonstatic_func),                                     strlen(kNonStaticFunc) - 1));  EXPECT_STREQ("n...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 5));  EXPECT_STREQ("...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 4));  EXPECT_STREQ("..", TrySymbolizeWithLimit((void *)(&nonstatic_func), 3));  EXPECT_STREQ(".", TrySymbolizeWithLimit((void *)(&nonstatic_func), 2));  EXPECT_STREQ("", TrySymbolizeWithLimit((void *)(&nonstatic_func), 1));  EXPECT_EQ(nullptr, TrySymbolizeWithLimit((void *)(&nonstatic_func), 0));}TEST(Symbolize, SymbolizeWithDemangling) {  Foo::func(100);  EXPECT_STREQ("Foo::func()", TrySymbolize((void *)(&Foo::func)));}TEST(Symbolize, SymbolizeSplitTextSections) {  EXPECT_STREQ("unlikely_func()", TrySymbolize((void *)(&unlikely_func)));  EXPECT_STREQ("hot_func()", TrySymbolize((void *)(&hot_func)));  EXPECT_STREQ("startup_func()", TrySymbolize((void *)(&startup_func)));  EXPECT_STREQ("exit_func()", TrySymbolize((void *)(&exit_func)));  EXPECT_STREQ("regular_func()", TrySymbolize((void *)(®ular_func)));}// Tests that verify that Symbolize stack footprint is within some limit.#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTIONstatic void *g_pc_to_symbolize;static char g_symbolize_buffer[4096];static char *g_symbolize_result;static void SymbolizeSignalHandler(int signo) {  if (absl::Symbolize(g_pc_to_symbolize, g_symbolize_buffer,                      sizeof(g_symbolize_buffer))) {    g_symbolize_result = g_symbolize_buffer;  } else {    g_symbolize_result = nullptr;  }}// Call Symbolize and figure out the stack footprint of this call.static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) {  g_pc_to_symbolize = pc;  *stack_consumed = absl::debugging_internal::GetSignalHandlerStackConsumption(      SymbolizeSignalHandler);  return g_symbolize_result;}static int GetStackConsumptionUpperLimit() {  // Symbolize stack consumption should be within 2kB.  int stack_consumption_upper_limit = 2048;#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \    defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)  // Account for sanitizer instrumentation requiring additional stack space.  stack_consumption_upper_limit *= 5;#endif  return stack_consumption_upper_limit;}TEST(Symbolize, SymbolizeStackConsumption) {  int stack_consumed = 0;  const char *symbol =      SymbolizeStackConsumption((void *)(&nonstatic_func), &stack_consumed);  EXPECT_STREQ("nonstatic_func", symbol);  EXPECT_GT(stack_consumed, 0);  EXPECT_LT(stack_consumed, GetStackConsumptionUpperLimit());  // The name of an internal linkage symbol is not specified; allow either a  // mangled or an unmangled name here.  symbol = SymbolizeStackConsumption((void *)(&static_func), &stack_consumed);  EXPECT_TRUE(strcmp("static_func", symbol) == 0 ||              strcmp("static_func()", symbol) == 0);  EXPECT_GT(stack_consumed, 0);  EXPECT_LT(stack_consumed, GetStackConsumptionUpperLimit());}TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {  Foo::func(100);  int stack_consumed = 0;  const char *symbol =      SymbolizeStackConsumption((void *)(&Foo::func), &stack_consumed);  EXPECT_STREQ("Foo::func()", symbol);  EXPECT_GT(stack_consumed, 0);  EXPECT_LT(stack_consumed, GetStackConsumptionUpperLimit());}#endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION#ifndef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE// Use a 64K page size for PPC.const size_t kPageSize = 64 << 10;// We place a read-only symbols into the .text section and verify that we can// symbolize them and other symbols after remapping them.const char kPadding0[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(.text) =    "";const char kPadding1[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(.text) =    "";static int FilterElfHeader(struct dl_phdr_info *info, size_t size, void *data) {  for (int i = 0; i < info->dlpi_phnum; i++) {    if (info->dlpi_phdr[i].p_type == PT_LOAD &&        info->dlpi_phdr[i].p_flags == (PF_R | PF_X)) {      const void *const vaddr =          absl::bit_cast<void *>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);      const auto segsize = info->dlpi_phdr[i].p_memsz;      const char *self_exe;      if (info->dlpi_name != nullptr && info->dlpi_name[0] != '\0') {        self_exe = info->dlpi_name;      } else {        self_exe = "/proc/self/exe";      }      absl::debugging_internal::RegisterFileMappingHint(          vaddr, reinterpret_cast<const char *>(vaddr) + segsize,          info->dlpi_phdr[i].p_offset, self_exe);      return 1;    }  }  return 1;}TEST(Symbolize, SymbolizeWithMultipleMaps) {  // Force kPadding0 and kPadding1 to be linked in.  if (volatile_bool) {    ABSL_RAW_LOG(INFO, "%s", kPadding0);    ABSL_RAW_LOG(INFO, "%s", kPadding1);  }  // Verify we can symbolize everything.  char buf[512];  memset(buf, 0, sizeof(buf));  absl::Symbolize(kPadding0, buf, sizeof(buf));  EXPECT_STREQ("kPadding0", buf);  memset(buf, 0, sizeof(buf));  absl::Symbolize(kPadding1, buf, sizeof(buf));  EXPECT_STREQ("kPadding1", buf);  // Specify a hint for the executable segment.  dl_iterate_phdr(FilterElfHeader, nullptr);  // Reload at least one page out of kPadding0, kPadding1  const char *ptrs[] = {kPadding0, kPadding1};  for (const char *ptr : ptrs) {    const int kMapFlags = MAP_ANONYMOUS | MAP_PRIVATE;    void *addr = mmap(nullptr, kPageSize, PROT_READ, kMapFlags, 0, 0);    ASSERT_NE(addr, MAP_FAILED);    // kPadding[0-1] is full of zeroes, so we can remap anywhere within it, but    // we ensure there is at least a full page of padding.    void *remapped = reinterpret_cast<void *>(        reinterpret_cast<uintptr_t>(ptr + kPageSize) & ~(kPageSize - 1ULL));    const int kMremapFlags = (MREMAP_MAYMOVE | MREMAP_FIXED);    void *ret = mremap(addr, kPageSize, kPageSize, kMremapFlags, remapped);    ASSERT_NE(ret, MAP_FAILED);  }  // Invalidate the symbolization cache so we are forced to rely on the hint.  absl::Symbolize(nullptr, buf, sizeof(buf));  // Verify we can still symbolize.  const char *expected[] = {"kPadding0", "kPadding1"};  const size_t offsets[] = {0, kPageSize, 2 * kPageSize, 3 * kPageSize};  for (int i = 0; i < 2; i++) {    for (size_t offset : offsets) {      memset(buf, 0, sizeof(buf));      absl::Symbolize(ptrs[i] + offset, buf, sizeof(buf));      EXPECT_STREQ(expected[i], buf);    }  }}// Appends string(*args->arg) to args->symbol_buf.static void DummySymbolDecorator(    const absl::debugging_internal::SymbolDecoratorArgs *args) {  std::string *message = static_cast<std::string *>(args->arg);  strncat(args->symbol_buf, message->c_str(),          args->symbol_buf_size - strlen(args->symbol_buf) - 1);}TEST(Symbolize, InstallAndRemoveSymbolDecorators) {  int ticket_a;  std::string a_message("a");  EXPECT_GE(ticket_a = absl::debugging_internal::InstallSymbolDecorator(                DummySymbolDecorator, &a_message),            0);  int ticket_b;  std::string b_message("b");  EXPECT_GE(ticket_b = absl::debugging_internal::InstallSymbolDecorator(                DummySymbolDecorator, &b_message),            0);  int ticket_c;  std::string c_message("c");  EXPECT_GE(ticket_c = absl::debugging_internal::InstallSymbolDecorator(                DummySymbolDecorator, &c_message),            0);  char *address = reinterpret_cast<char *>(1);  EXPECT_STREQ("abc", TrySymbolize(address++));  EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_b));  EXPECT_STREQ("ac", TrySymbolize(address++));  // Cleanup: remove all remaining decorators so other stack traces don't  // get mystery "ac" decoration.  EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_a));  EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_c));}// Some versions of Clang with optimizations enabled seem to be able// to optimize away the .data section if no variables live in the// section. This variable should get placed in the .data section, and// the test below checks for the existence of a .data section.static int in_data_section = 1;TEST(Symbolize, ForEachSection) {  int fd = TEMP_FAILURE_RETRY(open("/proc/self/exe", O_RDONLY));  ASSERT_NE(fd, -1);  std::vector<std::string> sections;  ASSERT_TRUE(absl::debugging_internal::ForEachSection(      fd, [§ions](const absl::string_view name, const ElfW(Shdr) &) {        sections.emplace_back(name);        return true;      }));  // Check for the presence of common section names.  EXPECT_THAT(sections, Contains(".text"));  EXPECT_THAT(sections, Contains(".rodata"));  EXPECT_THAT(sections, Contains(".bss"));  ++in_data_section;  EXPECT_THAT(sections, Contains(".data"));  close(fd);}#endif  // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE// x86 specific tests.  Uses some inline assembler.extern "C" {inline void *ABSL_ATTRIBUTE_ALWAYS_INLINE inline_func() {  void *pc = nullptr;#if defined(__i386__)  __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc));#elif defined(__x86_64__)  __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc));#endif  return pc;}void *ABSL_ATTRIBUTE_NOINLINE non_inline_func() {  void *pc = nullptr;#if defined(__i386__)  __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc));#elif defined(__x86_64__)  __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc));#endif  return pc;}void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() {#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) && \    (defined(__i386__) || defined(__x86_64__))  void *pc = non_inline_func();  const char *symbol = TrySymbolize(pc);  ABSL_RAW_CHECK(symbol != nullptr, "TestWithPCInsideNonInlineFunction failed");  ABSL_RAW_CHECK(strcmp(symbol, "non_inline_func") == 0,                 "TestWithPCInsideNonInlineFunction failed");  std::cout << "TestWithPCInsideNonInlineFunction passed" << std::endl;#endif}void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {#if defined(ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE) && \    (defined(__i386__) || defined(__x86_64__))  void *pc = inline_func();  // Must be inlined.  const char *symbol = TrySymbolize(pc);  ABSL_RAW_CHECK(symbol != nullptr, "TestWithPCInsideInlineFunction failed");  ABSL_RAW_CHECK(strcmp(symbol, __FUNCTION__) == 0,                 "TestWithPCInsideInlineFunction failed");  std::cout << "TestWithPCInsideInlineFunction passed" << std::endl;#endif}}// Test with a return address.void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE)  void *return_address = __builtin_return_address(0);  const char *symbol = TrySymbolize(return_address);  ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed");  ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed");  std::cout << "TestWithReturnAddress passed" << std::endl;#endif}#elif defined(_WIN32)#if !defined(ABSL_CONSUME_DLL)TEST(Symbolize, Basics) {  EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));  // The name of an internal linkage symbol is not specified; allow either a  // mangled or an unmangled name here.  const char *static_func_symbol = TrySymbolize((void *)(&static_func));  ASSERT_TRUE(static_func_symbol != nullptr);  EXPECT_TRUE(strstr(static_func_symbol, "static_func") != nullptr);  EXPECT_TRUE(nullptr == TrySymbolize(nullptr));}TEST(Symbolize, Truncation) {  constexpr char kNonStaticFunc[] = "nonstatic_func";  EXPECT_STREQ("nonstatic_func",               TrySymbolizeWithLimit((void *)(&nonstatic_func),                                     strlen(kNonStaticFunc) + 1));  EXPECT_STREQ("nonstatic_...",               TrySymbolizeWithLimit((void *)(&nonstatic_func),                                     strlen(kNonStaticFunc) + 0));  EXPECT_STREQ("nonstatic...",               TrySymbolizeWithLimit((void *)(&nonstatic_func),                                     strlen(kNonStaticFunc) - 1));  EXPECT_STREQ("n...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 5));  EXPECT_STREQ("...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 4));  EXPECT_STREQ("..", TrySymbolizeWithLimit((void *)(&nonstatic_func), 3));  EXPECT_STREQ(".", TrySymbolizeWithLimit((void *)(&nonstatic_func), 2));  EXPECT_STREQ("", TrySymbolizeWithLimit((void *)(&nonstatic_func), 1));  EXPECT_EQ(nullptr, TrySymbolizeWithLimit((void *)(&nonstatic_func), 0));}TEST(Symbolize, SymbolizeWithDemangling) {  const char *result = TrySymbolize((void *)(&Foo::func));  ASSERT_TRUE(result != nullptr);  EXPECT_TRUE(strstr(result, "Foo::func") != nullptr) << result;}#endif  // !defined(ABSL_CONSUME_DLL)#else  // Symbolizer unimplementedTEST(Symbolize, Unimplemented) {  char buf[64];  EXPECT_FALSE(absl::Symbolize((void *)(&nonstatic_func), buf, sizeof(buf)));  EXPECT_FALSE(absl::Symbolize((void *)(&static_func), buf, sizeof(buf)));  EXPECT_FALSE(absl::Symbolize((void *)(&Foo::func), buf, sizeof(buf)));}#endifint main(int argc, char **argv) {#if !defined(__EMSCRIPTEN__)  // Make sure kHpageTextPadding is linked into the binary.  if (volatile_bool) {    ABSL_RAW_LOG(INFO, "%s", kHpageTextPadding);  }#endif  // !defined(__EMSCRIPTEN__)#if ABSL_PER_THREAD_TLS  // Touch the per-thread variables.  symbolize_test_thread_small[0] = 0;  symbolize_test_thread_big[0] = 0;#endif  absl::InitializeSymbolizer(argv[0]);  testing::InitGoogleTest(&argc, argv);#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \    defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)  TestWithPCInsideInlineFunction();  TestWithPCInsideNonInlineFunction();  TestWithReturnAddress();#endif  return RUN_ALL_TESTS();}
 |