about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--absl/debugging/BUILD.bazel1
-rw-r--r--absl/debugging/CMakeLists.txt1
-rw-r--r--absl/debugging/symbolize.cc9
-rw-r--r--absl/debugging/symbolize_test.cc43
-rw-r--r--absl/debugging/symbolize_win32.inc74
-rw-r--r--absl/strings/substitute.cc55
-rw-r--r--absl/strings/substitute.h18
-rw-r--r--absl/strings/substitute_test.cc18
-rw-r--r--absl/types/span.h18
-rw-r--r--absl/types/variant_test.cc12
10 files changed, 223 insertions, 26 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
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index f739f8c20b2c..3b2005945095 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<uintptr_t>(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 <fill><+/-><digits>
+    // But...: if the fill character is '0', then it's <+/-><fill><digits>
+    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 5596a5dbf35a..5747d384d34e 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<hex value>". 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 7c9af6b22433..144df01ec184 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<const int*>(0x12345);
   std::string str = absl::Substitute("$0", int_p);
diff --git a/absl/types/span.h b/absl/types/span.h
index d365f17d5219..0ca30d1d0f08 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<int> 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 c4676c10c62d..262bd9446c18 100644
--- a/absl/types/variant_test.cc
+++ b/absl/types/variant_test.cc
@@ -119,19 +119,9 @@ struct ConversionException {};
 
 template <class T>
 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.