From 19b3c95727316cef3b0b40eaf37f6645a876f8d2 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 23 Apr 2018 15:50:21 -0700 Subject: - 3a9532fb2d6ae45c3cba44c9bb0dbdfc1558b7d3 Fix the description of Span::subspan(). by Abseil Team - bae1a1c21924bd31fa7315eff05ea6158d9e7947 Port the symbolizer to Windows. by Derek Mauro - 2253c04c1a4f39d9581772f1dc4491878aa3831f Support absl::Hex() and absl::Dec() as arguments to absl:... by Jorg Brown - 552c3ac259e9c254fda9244755487f3423d2fe4b Internal change by Jorg Brown GitOrigin-RevId: 3a9532fb2d6ae45c3cba44c9bb0dbdfc1558b7d3 Change-Id: I448133c9bb6d837037c12b45a9a16a7945049453 --- absl/debugging/BUILD.bazel | 1 + absl/debugging/CMakeLists.txt | 1 + absl/debugging/symbolize.cc | 9 ++++- absl/debugging/symbolize_test.cc | 43 ++++++++++++++++++++-- absl/debugging/symbolize_win32.inc | 74 ++++++++++++++++++++++++++++++++++++++ absl/strings/substitute.cc | 55 +++++++++++++++++++++++++++- absl/strings/substitute.h | 18 ++++++---- absl/strings/substitute_test.cc | 18 ++++++++++ absl/types/span.h | 18 +++++++--- absl/types/variant_test.cc | 12 +------ 10 files changed, 223 insertions(+), 26 deletions(-) create mode 100644 absl/debugging/symbolize_win32.inc diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index 8543200d..556bb383 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 8d2ec845..456d0727 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 355bf9ff..a35e24cc 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 b23a8011..c1090b8d 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 00000000..c300c50a --- /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 +#include +#pragma comment(lib, "DbgHelp") + +#include +#include + +#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::type buf; + SYMBOL_INFO *symbol = reinterpret_cast(&buf); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + if (!SymFromAddr(process, reinterpret_cast(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(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 diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index f739f8c2..3b200594 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc @@ -94,6 +94,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, assert(target == output->data() + output->size()); } +static const char kHexDigits[] = "0123456789abcdef"; Arg::Arg(const void* value) { static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2, "fix sizeof(scratch_)"); @@ -102,7 +103,6 @@ Arg::Arg(const void* value) { } else { char* ptr = scratch_ + sizeof(scratch_); uintptr_t num = reinterpret_cast(value); - static const char kHexDigits[] = "0123456789abcdef"; do { *--ptr = kHexDigits[num & 0xf]; num >>= 4; @@ -113,5 +113,58 @@ Arg::Arg(const void* value) { } } +// TODO(jorg): Don't duplicate so much code between here and str_cat.cc +Arg::Arg(Hex hex) { + char* const end = &scratch_[numbers_internal::kFastToBufferSize]; + char* writer = end; + uint64_t value = hex.value; + do { + *--writer = kHexDigits[value & 0xF]; + value >>= 4; + } while (value != 0); + + char* beg; + if (end - writer < hex.width) { + beg = end - hex.width; + std::fill_n(beg, writer - beg, hex.fill); + } else { + beg = writer; + } + + piece_ = absl::string_view(beg, end - beg); +} + +// TODO(jorg): Don't duplicate so much code between here and str_cat.cc +Arg::Arg(Dec dec) { + assert(dec.width <= numbers_internal::kFastToBufferSize); + char* const end = &scratch_[numbers_internal::kFastToBufferSize]; + char* const minfill = end - dec.width; + char* writer = end; + uint64_t value = dec.value; + bool neg = dec.neg; + while (value > 9) { + *--writer = '0' + (value % 10); + value /= 10; + } + *--writer = '0' + value; + if (neg) *--writer = '-'; + + ptrdiff_t fillers = writer - minfill; + if (fillers > 0) { + // Tricky: if the fill character is ' ', then it's <+/-> + // But...: if the fill character is '0', then it's <+/-> + bool add_sign_again = false; + if (neg && dec.fill == '0') { // If filling with '0', + ++writer; // ignore the sign we just added + add_sign_again = true; // and re-add the sign later. + } + writer -= fillers; + std::fill_n(writer, fillers, dec.fill); + if (add_sign_again) *--writer = '-'; + } + + piece_ = absl::string_view(writer, end - writer); +} + } // namespace substitute_internal } // namespace absl diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index 5596a5db..5747d384 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -76,7 +76,7 @@ #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" #include "absl/strings/numbers.h" -#include "absl/strings/str_join.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/strings/strip.h" @@ -113,10 +113,10 @@ class Arg { // what to do. Arg(char value) // NOLINT(runtime/explicit) : piece_(scratch_, 1) { scratch_[0] = value; } - Arg(short value) // NOLINT(runtime/explicit) + Arg(short value) // NOLINT(*) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(unsigned short value) // NOLINT(runtime/explicit) + Arg(unsigned short value) // NOLINT(*) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} Arg(int value) // NOLINT(runtime/explicit) @@ -125,16 +125,16 @@ class Arg { Arg(unsigned int value) // NOLINT(runtime/explicit) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(long value) // NOLINT(runtime/explicit) + Arg(long value) // NOLINT(*) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(unsigned long value) // NOLINT(runtime/explicit) + Arg(unsigned long value) // NOLINT(*) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(long long value) // NOLINT(runtime/explicit) + Arg(long long value) // NOLINT(*) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(unsigned long long value) // NOLINT(runtime/explicit) + Arg(unsigned long long value) // NOLINT(*) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} Arg(float value) // NOLINT(runtime/explicit) @@ -145,6 +145,10 @@ class Arg { } Arg(bool value) // NOLINT(runtime/explicit) : piece_(value ? "true" : "false") {} + + Arg(Hex hex); // NOLINT(runtime/explicit) + Arg(Dec dec); // NOLINT(runtime/explicit) + // `void*` values, with the exception of `char*`, are printed as // "0x". However, in the case of `nullptr`, "NULL" is printed. Arg(const void* value); // NOLINT(runtime/explicit) diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc index 7c9af6b2..144df01e 100644 --- a/absl/strings/substitute_test.cc +++ b/absl/strings/substitute_test.cc @@ -43,6 +43,24 @@ TEST(SubstituteTest, Substitute) { -1234567890, 3234567890U, -1234567890L, 3234567890UL, -int64_t{1234567890123456789}, uint64_t{9234567890123456789u})); + // Hex format + EXPECT_EQ("0 1 f ffff0ffff 0123456789abcdef", + absl::Substitute("$0$1$2$3$4 $5", // + absl::Hex(0), absl::Hex(1, absl::kSpacePad2), + absl::Hex(0xf, absl::kSpacePad2), + absl::Hex(int16_t{-1}, absl::kSpacePad5), + absl::Hex(int16_t{-1}, absl::kZeroPad5), + absl::Hex(0x123456789abcdef, absl::kZeroPad16))); + + // Dec format + EXPECT_EQ("0 115 -1-0001 81985529216486895", + absl::Substitute("$0$1$2$3$4 $5", // + absl::Dec(0), absl::Dec(1, absl::kSpacePad2), + absl::Dec(0xf, absl::kSpacePad2), + absl::Dec(int16_t{-1}, absl::kSpacePad5), + absl::Dec(int16_t{-1}, absl::kZeroPad5), + absl::Dec(0x123456789abcdef, absl::kZeroPad16))); + // Pointer. const int* int_p = reinterpret_cast(0x12345); std::string str = absl::Substitute("$0", int_p); diff --git a/absl/types/span.h b/absl/types/span.h index d365f17d..0ca30d1d 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -458,10 +458,20 @@ class Span { // Span::subspan() // - // Returns a `Span` starting at element `pos` and of length `len`, with - // proper bounds checking to ensure `len` does not exceed the ptr+size of the - // original array. (Spans whose `len` would point past the end of the array - // will throw a `std::out_of_range`.) + // Returns a `Span` starting at element `pos` and of length `len`. Both `pos` + // and `len` are of type `size_type` and thus non-negative. Parameter `pos` + // must be <= size(). Any `len` value that points past the end of the span + // will be trimmed to at most size() - `pos`. A default `len` value of `npos` + // ensures the returned subspan continues until the end of the span. + // + // Examples: + // + // std::vector vec = {10, 11, 12, 13}; + // absl::MakeSpan(vec).subspan(1, 2); // {11, 12} + // absl::MakeSpan(vec).subspan(2, 8); // {12, 13} + // absl::MakeSpan(vec).subspan(1); // {11, 12, 13} + // absl::MakeSpan(vec).subspan(4); // {} + // absl::MakeSpan(vec).subspan(5); // throws std::out_of_range constexpr Span subspan(size_type pos = 0, size_type len = npos) const { return (pos <= len_) ? Span(ptr_ + pos, span_internal::Min(len_ - pos, len)) diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc index c4676c10..262bd944 100644 --- a/absl/types/variant_test.cc +++ b/absl/types/variant_test.cc @@ -119,19 +119,9 @@ struct ConversionException {}; template struct ExceptionOnConversion { - // Suppress MSVC 2017 warning "noreturn function has a non-void return type". -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4646) -#endif // _MSC_VER - - [[noreturn]] operator T() const { // NOLINT(runtime/explicit) + operator T() const { // NOLINT(runtime/explicit) throw ConversionException(); } - -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER }; // Forces a variant into the valueless by exception state. -- cgit v1.2.3