diff options
Diffstat (limited to 'absl/debugging')
-rw-r--r-- | absl/debugging/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/debugging/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/debugging/symbolize.cc | 9 | ||||
-rw-r--r-- | absl/debugging/symbolize_test.cc | 43 | ||||
-rw-r--r-- | absl/debugging/symbolize_win32.inc | 74 |
5 files changed, 125 insertions, 3 deletions
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index 8543200df87d..556bb38394c9 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -46,6 +46,7 @@ cc_library( "symbolize.cc", "symbolize_elf.inc", "symbolize_unimplemented.inc", + "symbolize_win32.inc", ], hdrs = [ "internal/symbolize.h", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 8d2ec845d67b..456d07271b82 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -45,6 +45,7 @@ list(APPEND SYMBOLIZE_SRC "symbolize.cc" "symbolize_elf.inc" "symbolize_unimplemented.inc" + "symbolize_win32.inc" "internal/demangle.cc" ${DEBUGGING_PUBLIC_HEADERS} ${DEBUGGING_INTERNAL_HEADERS} diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index 355bf9ff9522..a35e24cc29a4 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc @@ -14,8 +14,15 @@ #include "absl/debugging/symbolize.h" -#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE +#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) #include "absl/debugging/symbolize_elf.inc" +#elif defined(_WIN32) && defined(_DEBUG) +// The Windows Symbolizer only works in debug mode. Note that _DEBUG +// is the macro that defines whether or not MS C-Runtime debug info is +// available. Note that the PDB files containing the debug info must +// also be available to the program at runtime for the symbolizer to +// work. +#include "absl/debugging/symbolize_win32.inc" #else #include "absl/debugging/symbolize_unimplemented.inc" #endif diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index b23a8011990b..c1090b8dc437 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -90,8 +90,6 @@ static constexpr size_t kHpageSize = 1 << 21; const char kHpageTextPadding[kHpageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE( ".text") = ""; -#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE - static char try_symbolize_buffer[4096]; // A wrapper function for absl::Symbolize() to make the unit test simple. The @@ -120,6 +118,8 @@ static const char *TrySymbolize(void *pc) { return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer)); } +#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE + TEST(Symbolize, Cached) { // Compilers should give us pointers to them. EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func))); @@ -442,6 +442,45 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { #endif } +#elif defined(_WIN32) && defined(_DEBUG) + +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; +} + #else // Symbolizer unimplemented TEST(Symbolize, Unimplemented) { diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc new file mode 100644 index 000000000000..c300c50a4e2e --- /dev/null +++ b/absl/debugging/symbolize_win32.inc @@ -0,0 +1,74 @@ +// 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 +// +// http://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. + +// See "Retrieving Symbol Information by Address": +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx + +#include <windows.h> +#include <DbgHelp.h> +#pragma comment(lib, "DbgHelp") + +#include <algorithm> +#include <cstring> + +#include "absl/base/internal/raw_logging.h" + +namespace absl { + +static HANDLE process = NULL; + +void InitializeSymbolizer(const char *argv0) { + if (process != nullptr) { + return; + } + process = GetCurrentProcess(); + + // Symbols are not loaded until a reference is made requiring the + // symbols be loaded. This is the fastest, most efficient way to use + // the symbol handler. + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME); + if (!SymInitialize(process, nullptr, true)) { + // GetLastError() returns a Win32 DWORD, but we assign to + // unsigned long to simplify the ABSL_RAW_LOG case below. The uniform + // initialization guarantees this is not a narrowing conversion. + const unsigned long long error{GetLastError()}; // NOLINT(runtime/int) + ABSL_RAW_LOG(FATAL, "SymInitialize() failed: %llu", error); + } +} + +bool Symbolize(const void *pc, char *out, int out_size) { + if (out_size <= 0) { + return false; + } + std::aligned_storage<sizeof(SYMBOL_INFO) + MAX_SYM_NAME, + alignof(SYMBOL_INFO)>::type buf; + SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(&buf); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) { + return false; + } + strncpy(out, symbol->Name, out_size); + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = + std::min<int>(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + return true; +} + +} // namespace absl |