diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index cbeb96c..842c6b3 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -454,6 +454,7 @@ "types/internal/span.h" "types/optional.h" "types/optional_ref.h" + "types/source_location.h" "types/span.h" "types/variant.h" "utility/utility.h"
diff --git a/CMakeLists.txt b/CMakeLists.txt index a31c6a9..3583e3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -227,7 +227,7 @@ # Handle features that require at least C++20. if (ABSL_INTERNAL_AT_LEAST_CXX20) - foreach(FEATURE "ORDERING") + foreach(FEATURE "ORDERING" "SOURCE_LOCATION") string(REPLACE "#define ABSL_OPTION_USE_STD_${FEATURE} 2" "#define ABSL_OPTION_USE_STD_${FEATURE} 1"
diff --git a/absl/base/config.h b/absl/base/config.h index 82e8009..b707654 100644 --- a/absl/base/config.h +++ b/absl/base/config.h
@@ -531,6 +531,42 @@ #define ABSL_HAVE_STD_VARIANT 1 #define ABSL_USES_STD_VARIANT 1 +// ABSL_HAVE_STD_SOURCE_LOCATION +// +// Checks whether C++20 std::source_location is available. +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION +#error "ABSL_HAVE_STD_SOURCE_LOCATION cannot be directly set." +#elif (defined(__cpp_lib_source_location) && \ + __cpp_lib_source_location >= 201907L) || \ + (defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L) +#ifdef __has_include +#if __has_include(<source_location>) +#define ABSL_HAVE_STD_SOURCE_LOCATION 1 +#endif +#else +// No __has_include support, so just assume C++ language version is correct. +#define ABSL_HAVE_STD_SOURCE_LOCATION 1 +#endif +#endif + +// ABSL_USES_STD_SOURCE_LOCATION +// +// Indicates whether absl::SourceLocation is an alias for std::source_location. +#if !defined(ABSL_OPTION_USE_STD_SOURCE_LOCATION) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_SOURCE_LOCATION == 0 || \ + (ABSL_OPTION_USE_STD_SOURCE_LOCATION == 2 && \ + !defined(ABSL_HAVE_STD_SOURCE_LOCATION)) +#undef ABSL_USES_STD_SOURCE_LOCATION +#elif ABSL_OPTION_USE_STD_SOURCE_LOCATION == 1 || \ + (ABSL_OPTION_USE_STD_SOURCE_LOCATION == 2 && \ + defined(ABSL_HAVE_STD_SOURCE_LOCATION)) +#define ABSL_USES_STD_SOURCE_LOCATION 1 +#else +#error options.h is misconfigured. +#endif + // ABSL_HAVE_STD_ORDERING // // Checks whether C++20 std::{partial,weak,strong}_ordering are available. @@ -799,6 +835,14 @@ #define ABSL_INTERNAL_HAVE_ARM_NEON 1 #endif +#if ABSL_HAVE_BUILTIN(__builtin_LINE) && ABSL_HAVE_BUILTIN(__builtin_FILE) +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#elif defined(__GNUC__) && !defined(__clang__) && 5 <= __GNUC__ && __GNUC__ < 10 +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#elif defined(_MSC_VER) && _MSC_VER >= 1926 +#define ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE 1 +#endif + // ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of // constant evaluation support through `absl::is_constant_evaluated`. #ifdef ABSL_HAVE_CONSTANT_EVALUATED
diff --git a/absl/base/fast_type_id.h b/absl/base/fast_type_id.h index a1214da..6e4c573 100644 --- a/absl/base/fast_type_id.h +++ b/absl/base/fast_type_id.h
@@ -31,7 +31,7 @@ } // namespace base_internal // The type returned by `absl::FastTypeId<T>()`. -class FastTypeIdType { +class FastTypeIdType final { public: // Creates a value that does not correspond to any type. This value is // distinct from any value returned by `FastTypeId<T>()`.
diff --git a/absl/base/options.h b/absl/base/options.h index 6f48e75..cfacc6e 100644 --- a/absl/base/options.h +++ b/absl/base/options.h
@@ -73,6 +73,34 @@ // Type Compatibility Options // ----------------------------------------------------------------------------- +// ABSL_OPTION_USE_STD_SOURCE_LOCATION +// +// This option controls whether absl::SourceLocation is implemented as an alias +// to the std::source_location type, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++17 +// support, and is expected to run on every toolchain we support, and to +// properly capture source location information on every toolchain that supports +// the necessary built-ins (such as `__builtin_LINE`). +// +// A value of 1 means to use aliases. This requires that all code using Abseil +// is built in C++20 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if working std::source_location types are available. +// This option is useful when you are building your program from source. It +// should not be used otherwise -- for example, if you are distributing Abseil +// in a binary package manager -- since in mode 2, they will name different +// types, with different mangled names and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// the source location type is an alias of std::source_location type, use the +// feature macro ABSL_USES_STD_SOURCE_LOCATION. +// +#define ABSL_OPTION_USE_STD_SOURCE_LOCATION 2 + // ABSL_OPTION_USE_STD_ORDERING // // This option controls whether absl::{partial,weak,strong}_ordering are
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 5e9a483..1a9fa1d 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -459,26 +459,41 @@ // The size and also has additionally // 1) one bit that stores whether we have infoz. -// 2) PerTableSeed::kBitCount bits for the seed. +// 2) PerTableSeed::kBitCount bits for the seed. (For SOO tables, the lowest +// bit of the seed is repurposed to track if sampling has been tried). class HashtableSize { public: static constexpr size_t kSizeBitCount = 64 - PerTableSeed::kBitCount - 1; explicit HashtableSize(uninitialized_tag_t) {} explicit HashtableSize(no_seed_empty_tag_t) : data_(0) {} - explicit HashtableSize(full_soo_tag_t) : data_(kSizeOneNoMetadata) {} + HashtableSize(full_soo_tag_t, bool has_tried_sampling) + : data_(kSizeOneNoMetadata | + (has_tried_sampling ? kSooHasTriedSamplingMask : 0)) {} // Returns actual size of the table. size_t size() const { return static_cast<size_t>(data_ >> kSizeShift); } void increment_size() { data_ += kSizeOneNoMetadata; } void increment_size(size_t size) { - data_ += static_cast<uint64_t>(size) * kSizeOneNoMetadata; + data_ += static_cast<uint64_t>(size) << kSizeShift; } void decrement_size() { data_ -= kSizeOneNoMetadata; } // Returns true if the table is empty. bool empty() const { return data_ < kSizeOneNoMetadata; } - // Sets the size to zero, but keeps all the metadata bits. - void set_size_to_zero_keep_metadata() { data_ = data_ & kMetadataMask; } + + // Returns true if an empty SOO table has already queried should_sample_soo(). + bool soo_has_tried_sampling() const { + return (data_ & kSooHasTriedSamplingMask) != 0; + } + + // Records that an empty SOO table has tried sampling. + void set_soo_has_tried_sampling() { data_ |= kSooHasTriedSamplingMask; } + + // Sets the size, but keeps all the metadata bits. + void set_size_keep_metadata(size_t size) { + data_ = + (data_ & kMetadataMask) | (static_cast<uint64_t>(size) << kSizeShift); + } PerTableSeed seed() const { return PerTableSeed(static_cast<size_t>(data_) & kSeedMask); @@ -509,6 +524,10 @@ private: void set_seed(uint16_t seed) { data_ = (data_ & ~kSeedMask) | seed; } + // Bit layout of `data_`: + // [63 ... 17] (47 bits) : size + // [16] (1 bit) : has_infoz + // [15 ... 0] (16 bits) : seed static constexpr size_t kSizeShift = 64 - kSizeBitCount; static constexpr uint64_t kSizeOneNoMetadata = uint64_t{1} << kSizeShift; static constexpr uint64_t kMetadataMask = kSizeOneNoMetadata - 1; @@ -516,6 +535,9 @@ (uint64_t{1} << PerTableSeed::kBitCount) - 1; // The next bit after the seed. static constexpr uint64_t kHasInfozMask = kSeedMask + 1; + // For SOO tables, the seed is unused, and bit 0 is repurposed to track + // whether the table has already queried should_sample_soo(). + static constexpr uint64_t kSooHasTriedSamplingMask = 1; uint64_t data_; }; @@ -907,8 +929,8 @@ public: explicit CommonFields(soo_tag_t) : capacity_(SooCapacity()), size_(no_seed_empty_tag_t{}) {} - explicit CommonFields(full_soo_tag_t) - : capacity_(SooCapacity()), size_(full_soo_tag_t{}) {} + explicit CommonFields(full_soo_tag_t, bool has_tried_sampling) + : capacity_(SooCapacity()), size_(full_soo_tag_t{}, has_tried_sampling) {} explicit CommonFields(non_soo_tag_t) : capacity_(0), size_(no_seed_empty_tag_t{}) {} // For use in swapping. @@ -963,14 +985,14 @@ // The number of filled slots. size_t size() const { return size_.size(); } // Sets the size to zero, but keeps hashinfoz bit and seed. - void set_size_to_zero() { size_.set_size_to_zero_keep_metadata(); } + void set_size_to_zero() { size_.set_size_keep_metadata(0); } void set_empty_soo() { AssertInSooMode(); - size_ = HashtableSize(no_seed_empty_tag_t{}); + size_.set_size_keep_metadata(0); } void set_full_soo() { AssertInSooMode(); - size_ = HashtableSize(full_soo_tag_t{}); + size_.set_size_keep_metadata(1); } void increment_size() { ABSL_SWISSTABLE_ASSERT(size() < capacity()); @@ -985,6 +1007,8 @@ size_.decrement_size(); } bool empty() const { return size_.empty(); } + void set_soo_has_tried_sampling() { size_.set_soo_has_tried_sampling(); } + bool soo_has_tried_sampling() const { return size_.soo_has_tried_sampling(); } // The seed used for the hash function. PerTableSeed seed() const { return size_.seed(); } @@ -2271,7 +2295,8 @@ // Note: we avoid using exchange for better generated code. settings_(PolicyTraits::transfer_uses_memcpy() || !that.is_full_soo() ? std::move(that.common()) - : CommonFields{full_soo_tag_t{}}, + : CommonFields{full_soo_tag_t{}, + that.common().soo_has_tried_sampling()}, that.hash_ref(), that.eq_ref(), that.char_alloc_ref()) { if (!PolicyTraits::transfer_uses_memcpy() && that.is_full_soo()) { transfer(soo_slot(), that.soo_slot()); @@ -3011,12 +3036,23 @@ } } - // Returns true if the table needs to be sampled. + // Returns true if the table needs to be sampled. This keeps track of whether + // sampling has already been evaluated and ensures that it can only return + // true on its first evaluation. All subsequent calls will return false. + // // This should be called on insertion into an empty SOO table and in copy // construction when the size can fit in SOO capacity. - bool should_sample_soo() const { + bool should_sample_soo() { ABSL_SWISSTABLE_ASSERT(is_soo()); if (!ShouldSampleHashtablezInfoForAlloc<CharAlloc>()) return false; + if (common().soo_has_tried_sampling()) { + // Already evaluated sampling on this SOO table; do not re-evaluate + // sampling each time it transitions from empty to full SOO state. + return false; + } + // TODO: b/396049910 -- consider managing this flag on the 1->0 size + // transition of SOO tables rather than the 0->1 transition. + common().set_soo_has_tried_sampling(); return ABSL_PREDICT_FALSE(ShouldSampleNextTable()); }
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index fd99b2c..9c33e3c 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc
@@ -2932,6 +2932,70 @@ } } +// Verifies that repeated insertions and erasures on an SOO table do not cause +// the sampling decision to be evaluated multiple times, preventing +// oversampling. +TEST(RawHashSamplerTest, SooTableRepeatedInsertEraseDoesNotOversample) { + if (SooInt32Table().capacity() != SooCapacity()) { + CHECK_LT(sizeof(void*), 8) << "missing SOO coverage"; + GTEST_SKIP() << "not SOO on this platform"; + } + std::vector<const HashtablezInfo*> infos = + SampleSooMutation([](SooInt32Table& t) { + for (int i = 0; i < 10; ++i) { + t.insert(1); + t.erase(1); + } + }); + + // SampleSooMutation checks EXPECT_NEAR(sampled/total, 1%, 0.5%). + // If the sampling logic is incorrectly evaluated on every 0->1 element + // transition, the chance of being sampled per table approaches 10% (1 - + // 0.99^10), which is enough to cause this test to fail. By passing, this test + // verifies that the sampling decision is evaluated exactly once per SOO table + // instance. +} + +// Verifies that copy-constructing or copy-assigning an SOO table does not +// incorrectly trigger new sampling evaluations. +TEST(RawHashSamplerTest, SooTableCopyDoesNotOversample) { + if (SooInt32Table().capacity() != SooCapacity()) { + CHECK_LT(sizeof(void*), 8) << "missing SOO coverage"; + GTEST_SKIP() << "not SOO on this platform"; + } + std::vector<const HashtablezInfo*> infos = + SampleSooMutation([](SooInt32Table& t) { + t.insert(1); + t.erase(1); + SooInt32Table t_copy(t); + for (int i = 0; i < 10; ++i) { + t_copy.insert(1); + t_copy.erase(1); + } + t = std::move(t_copy); + }); +} + +// Verifies that move-constructing or move-assigning an SOO table correctly +// transfers the sampling state and does not trigger oversampling. +TEST(RawHashSamplerTest, SooTableMoveDoesNotOversample) { + if (SooInt32Table().capacity() != SooCapacity()) { + CHECK_LT(sizeof(void*), 8) << "missing SOO coverage"; + GTEST_SKIP() << "not SOO on this platform"; + } + std::vector<const HashtablezInfo*> infos = + SampleSooMutation([](SooInt32Table& t) { + t.insert(1); + t.erase(1); + SooInt32Table t_moved(std::move(t)); + for (int i = 0; i < 10; ++i) { + t_moved.insert(1); + t_moved.erase(1); + } + t = std::move(t_moved); + }); +} + TEST(RawHashSamplerTest, SooTableReserveToEmpty) { if (SooInt32Table().capacity() != SooCapacity()) { CHECK_LT(sizeof(void*), 8) << "missing SOO coverage"; @@ -3029,6 +3093,63 @@ ASSERT_EQ(info->total_probe_length, 0); } } + +// Verifies that a moved-from table does not retain the sampled state of the +// original table, allowing it to be used without incorrectly updating global +// sampling stats. +TEST(RawHashSamplerTest, MovedFromTableIsNotSampled) { + if (SooInt32Table().capacity() != SooCapacity()) { + CHECK_LT(sizeof(void*), 8) << "missing SOO coverage"; + GTEST_SKIP() << "not SOO on this platform"; + } + + SetSamplingRateTo1Percent(); + auto& sampler = GlobalHashtablezSampler(); + size_t start_size = 0; + absl::flat_hash_set<const HashtablezInfo*> preexisting_info; + sampler.Iterate([&](const HashtablezInfo& info) { + preexisting_info.insert(&info); + ++start_size; + }); + + SooInt32Table t1; + // Loop until t1 is sampled + while (true) { + t1 = SooInt32Table(); + t1.insert(1); + size_t new_size = 0; + sampler.Iterate([&](const HashtablezInfo&) { ++new_size; }); + if (new_size > start_size) break; + } + + // Move the table + SooInt32Table t2 = std::move(t1); + + // Disable sampling to ensure any new sampling is a bug. + SetHashtablezEnabled(false); + + // t2 is now the sampled table. t1 is moved-from. + // We want to verify that t2 is still sampled, and that t1 isn't sampled + // anymore, even if we insert a new entry into it. + t1.clear(); // Must clear before using a moved-from table. + t1.insert(2); + t2.insert(2); + + // Verify no new sample was generated, and t2's sample size is now 2. + size_t final_size = 0; + const HashtablezInfo* latest_info = nullptr; + size_t dropped = sampler.Iterate([&](const HashtablezInfo& info) { + ++final_size; + if (!preexisting_info.contains(&info)) { + latest_info = &info; + } + }); + EXPECT_EQ(0, dropped); + EXPECT_EQ(final_size, start_size + 1); + ASSERT_NE(latest_info, nullptr); + EXPECT_EQ(latest_info->size.load(std::memory_order_relaxed), 2); +} + #endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index a5c736c..e6f11e8 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel
@@ -135,10 +135,13 @@ "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:malloc_internal", + "//absl/cleanup", "//absl/log", "//absl/log:check", "//absl/memory", "//absl/strings", + "//absl/strings:str_format", "@googletest//:gtest", ], )
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index d8249fe..fbdb550 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt
@@ -107,12 +107,15 @@ DEPS absl::base absl::check + absl::cleanup absl::config absl::core_headers absl::log + absl::malloc_internal absl::memory absl::stack_consumption absl::strings + absl::str_format absl::symbolize GTest::gmock )
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h index 509f426..bd2c9c7 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h
@@ -22,6 +22,7 @@ #include <cstddef> #include <cstdint> +#include <memory> #include "absl/base/config.h" #include "absl/strings/string_view.h" @@ -35,7 +36,6 @@ #include <elf.h> #include <link.h> // For ElfW() macro. #include <functional> -#include <string> namespace absl { ABSL_NAMESPACE_BEGIN @@ -78,6 +78,7 @@ ABSL_NAMESPACE_BEGIN namespace debugging_internal { +// Legacy stateless symbol decorator API. Will be removed soon. struct SymbolDecoratorArgs { // The program counter we are getting symbolic name for. const void *pc; @@ -101,13 +102,13 @@ // User-provided argument void* arg; }; -using SymbolDecorator = void (*)(const SymbolDecoratorArgs *); +using LegacySymbolDecorator = void (*)(const SymbolDecoratorArgs *); // Installs a function-pointer as a decorator. Returns a value less than zero // if the system cannot install the decorator. Otherwise, returns a unique // identifier corresponding to the decorator. This identifier can be used to // uninstall the decorator - See RemoveSymbolDecorator() below. -int InstallSymbolDecorator(SymbolDecorator decorator, void* arg); +int InstallSymbolDecorator(LegacySymbolDecorator decorator, void* arg); // Removes a previously installed function-pointer decorator. Parameter "ticket" // is the return-value from calling InstallSymbolDecorator(). @@ -117,6 +118,55 @@ // symbolization is currently in progress. bool RemoveAllSymbolDecorators(); +class SymbolDecorator; + +class SymbolDecoratorDeleter { + public: + void operator()(SymbolDecorator* ptr); +}; + +using SymbolDecoratorPtr = + std::unique_ptr<SymbolDecorator, SymbolDecoratorDeleter>; + +// Represents an object that can add additional information to a symbol +// name. +class SymbolDecorator { + public: + // The signature of a factory function used to register and create a symbol + // decorator. This function may be called from a signal handler, so it must + // use an async-signal-safe allocation mechanism to allocate the returned + // object. + using Factory = SymbolDecoratorPtr(int fd); + + virtual ~SymbolDecorator() = default; + + // Decorates symbol name with additional information. + // + // pc represents the program counter we are getting symbolic name for. + // relocation is difference between the link-time and the load-time address. + // symbol_buf and symbol_buf_size represent the output buffer and its size. + // Note that the buffer may not be empty -- default symbolizer may have + // already produced some output. You are free to replace or augment the + // contents (within the symbol_buf_size limit). tmp_buf and tmp_buf_size + // represent temporary scratch space and its size. Use that space in + // preference to allocating your own stack buffer to conserve stack. + // + // This function will not be called concurrently for the same object, but it + // may be called from a signal handler, so it must avoid any operation that is + // not async-signal-safe. However, it does not have to be reentrant -- if it + // is interrupted by a signal and the handler tries to symbolize, the request + // will go to a new SymbolDecorator instance. + virtual void Decorate( + const void* pc, + ptrdiff_t relocation, + char* symbol_buf, size_t symbol_buf_size, + char* tmp_buf, size_t tmp_buf_size) const = 0; +}; + +// Registers a new symbol decorator factory and returns the previous one. +SymbolDecorator::Factory* SetSymbolDecoratorFactory( + SymbolDecorator::Factory* factory); + // Registers an address range to a file mapping. // // Preconditions:
diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index 344436f..147fda9 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc
@@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -// SKIP_ABSL_INLINE_NAMESPACE_CHECK - #include "absl/debugging/symbolize.h" +#include "absl/base/config.h" +#include "absl/base/internal/low_level_alloc.h" +#include "absl/debugging/internal/symbolize.h" + #ifdef _WIN32 #include <winapifamily.h> #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) @@ -42,3 +44,19 @@ #else #include "absl/debugging/symbolize_unimplemented.inc" #endif + + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace debugging_internal { + +void SymbolDecoratorDeleter::operator()(SymbolDecorator* ptr) { + ptr->~SymbolDecorator(); + base_internal::LowLevelAlloc::Free(ptr); +} + +} // namespace debugging_internal + +ABSL_NAMESPACE_END +} // namespace absl
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 0317bbc..5630cc2 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc
@@ -67,6 +67,7 @@ #include <cstdio> #include <cstdlib> #include <cstring> +#include <memory> #include "absl/base/casts.h" #include "absl/base/dynamic_annotations.h" @@ -148,7 +149,7 @@ const int kMaxDecorators = 10; // Seems like a reasonable upper limit. struct InstalledSymbolDecorator { - SymbolDecorator fn; + LegacySymbolDecorator fn; void *arg; int ticket; }; @@ -156,6 +157,8 @@ int g_num_decorators; InstalledSymbolDecorator g_decorators[kMaxDecorators]; +std::atomic<SymbolDecorator::Factory*> g_decorator_factory = nullptr; + struct FileMappingHint { const void *start; const void *end; @@ -190,32 +193,23 @@ } struct ObjFile { - ObjFile() - : filename(nullptr), - start_addr(nullptr), - end_addr(nullptr), - offset(0), - fd(-1), - elf_type(-1) { - SafeMemZero(&elf_header, sizeof(elf_header)); - SafeMemZero(&phdr[0], sizeof(phdr)); - } - - char *filename; - const void *start_addr; - const void *end_addr; - uint64_t offset; + char *filename = nullptr; + const void *start_addr = nullptr; + const void *end_addr = nullptr; + uint64_t offset = 0; // The following fields are initialized on the first access to the // object file. - int fd; - int elf_type; - ElfW(Ehdr) elf_header; + int fd = -1; + int elf_type = -1; + ElfW(Ehdr) elf_header = {}; // PT_LOAD program header describing executable code. // Normally we expect just one, but SWIFT binaries have two. // CUDA binaries have 3 (see cr/473913254 description). - std::array<ElfW(Phdr), 4> phdr; + std::array<ElfW(Phdr), 4> phdr = {}; + + SymbolDecoratorPtr decorator = {}; }; // Build 4-way associative cache for symbols. Within each cache line, symbols @@ -266,7 +260,8 @@ static_cast<ObjFile *>(base_internal::LowLevelAlloc::AllocWithArena( new_allocated * sizeof(*new_obj_), base_internal::SigSafeArena())); if (obj_) { - memcpy(new_obj_, obj_, allocated_ * sizeof(*new_obj_)); + std::uninitialized_move_n(obj_, size_, new_obj_); + std::destroy_n(obj_, size_); base_internal::LowLevelAlloc::Free(obj_); } obj_ = new_obj_; @@ -309,6 +304,8 @@ ~Symbolizer(); const char *GetSymbol(const void *const pc); + void UpdateDecoratorsIfNeeded(SymbolDecorator::Factory* decorator_factory); + private: char *CopyString(const char *s) { size_t len = strlen(s); @@ -345,6 +342,7 @@ }; AddrMap addr_map_; + SymbolDecorator::Factory* decorator_factory_ = nullptr; bool ok_; bool addr_map_read_; @@ -422,11 +420,14 @@ base_internal::InitSigSafeArena(); Symbolizer *symbolizer = g_cached_symbolizer.exchange(nullptr, std::memory_order_acquire); - if (symbolizer != nullptr) { - return symbolizer; + if (symbolizer == nullptr) { + void* ptr = base_internal::LowLevelAlloc::AllocWithArena( + SymbolizerSize(), base_internal::SigSafeArena()); + symbolizer = new (ptr) Symbolizer(); } - return new (base_internal::LowLevelAlloc::AllocWithArena( - SymbolizerSize(), base_internal::SigSafeArena())) Symbolizer(); + symbolizer->UpdateDecoratorsIfNeeded( + g_decorator_factory.load(std::memory_order_acquire)); + return symbolizer; } // Set g_cached_symbolize_state to s if it is null, otherwise @@ -443,7 +444,7 @@ Symbolizer::Symbolizer() : ok_(true), addr_map_read_(false) { for (SymbolCacheLine &symbol_cache_line : symbol_cache_) { - for (size_t j = 0; j < ABSL_ARRAYSIZE(symbol_cache_line.name); ++j) { + for (size_t j = 0; j < std::size(symbol_cache_line.name); ++j) { symbol_cache_line.pc[j] = nullptr; symbol_cache_line.name[j] = nullptr; symbol_cache_line.age[j] = 0; @@ -460,6 +461,26 @@ ClearAddrMap(); } +void Symbolizer::UpdateDecoratorsIfNeeded( + SymbolDecorator::Factory* decorator_factory) { + if (decorator_factory_ == decorator_factory) return; + + for (SymbolCacheLine& symbol_cache_line : symbol_cache_) { + for (size_t j = 0; j < std::size(symbol_cache_line.name); ++j) { + symbol_cache_line.pc[j] = nullptr; + base_internal::LowLevelAlloc::Free(symbol_cache_line.name[j]); + symbol_cache_line.name[j] = nullptr; + symbol_cache_line.age[j] = 0; + } + } + + decorator_factory_ = decorator_factory; + for (size_t i = 0, end = addr_map_.Size(); i < end; ++i) { + ObjFile* obj = addr_map_.At(i); + obj->decorator.reset(); + } +} + // We don't use assert() since it's not guaranteed to be // async-signal-safe. Instead we define a minimal assertion // macro. So far, we don't need pretty printing for __FILE__, etc. @@ -1387,7 +1408,8 @@ obj->fd = fd; } -static bool MaybeInitializeObjFile(ObjFile *obj) { +static bool MaybeInitializeObjFile( + ObjFile* obj, SymbolDecorator::Factory* decorator_factory) { if (obj->fd < 0) { obj->fd = OpenReadOnlyWithHighFD(obj->filename); @@ -1469,6 +1491,9 @@ return false; } } + if (decorator_factory != nullptr && obj->decorator == nullptr) { + obj->decorator = decorator_factory(obj->fd); + } return true; } @@ -1484,7 +1509,7 @@ ptrdiff_t relocation = 0; int fd = -1; if (obj != nullptr) { - if (MaybeInitializeObjFile(obj)) { + if (MaybeInitializeObjFile(obj, decorator_factory_)) { const size_t start_addr = reinterpret_cast<size_t>(obj->start_addr); if (obj->elf_type == ET_DYN && start_addr >= obj->offset) { // This object was relocated. @@ -1561,6 +1586,10 @@ } g_decorators_mu.unlock(); } + if (obj != nullptr && obj->decorator != nullptr) { + obj->decorator->Decorate(pc, relocation, symbol_buf_, sizeof(symbol_buf_), + tmp_buf_, sizeof(tmp_buf_)); + } if (symbol_buf_[0] == '\0') { return nullptr; } @@ -1604,7 +1633,9 @@ #endif } -bool RemoveAllSymbolDecorators(void) { +bool RemoveAllSymbolDecorators() { + SetSymbolDecoratorFactory(nullptr); + if (!g_decorators_mu.try_lock()) { // Someone else is using decorators. Get out. return false; @@ -1633,7 +1664,7 @@ return true; // Decorator is known to be removed. } -int InstallSymbolDecorator(SymbolDecorator decorator, void *arg) { +int InstallSymbolDecorator(LegacySymbolDecorator decorator, void *arg) { static int ticket = 0; if (!g_decorators_mu.try_lock()) { @@ -1651,6 +1682,11 @@ return ret; } +SymbolDecorator::Factory* SetSymbolDecoratorFactory( + SymbolDecorator::Factory* factory) { + return g_decorator_factory.exchange(factory, std::memory_order_acq_rel); +} + bool RegisterFileMappingHint(const void *start, const void *end, uint64_t offset, const char *filename) { SAFE_ASSERT(start <= end);
diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 2dd5da5..5e1e65d 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc
@@ -13,6 +13,10 @@ // limitations under the License. #include "absl/debugging/symbolize.h" +#include <cstddef> + +#include "absl/debugging/internal/symbolize.h" +#include "absl/strings/str_format.h" #ifdef __EMSCRIPTEN__ #include <emscripten.h> @@ -32,8 +36,10 @@ #include "absl/base/attributes.h" #include "absl/base/casts.h" #include "absl/base/config.h" +#include "absl/base/internal/low_level_alloc.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/optimization.h" +#include "absl/cleanup/cleanup.h" #include "absl/debugging/internal/stack_consumption.h" #include "absl/log/check.h" #include "absl/log/log.h" @@ -411,6 +417,43 @@ EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_c)); } +template <char C> +class TestSymbolDecorator final + : public absl::debugging_internal::SymbolDecorator { + public: + static absl::debugging_internal::SymbolDecoratorPtr Factory(int /*fd*/) { + void* ptr = absl::base_internal::LowLevelAlloc::AllocWithArena( + sizeof(TestSymbolDecorator), absl::base_internal::SigSafeArena()); + return absl::debugging_internal::SymbolDecoratorPtr( + new (ptr) TestSymbolDecorator()); + } + + void Decorate(const void* /*pc*/, ptrdiff_t /*relocation*/, char* symbol_buf, + size_t symbol_buf_size, char* /*tmp_buf*/, + size_t /*tmp_buf_size*/) const override { + const size_t len = strlen(symbol_buf); + absl::SNPrintF(symbol_buf + len, symbol_buf_size - len, " hello %c", C); + } +}; + +TEST(Symbolize, SetSymbolDecorator) { + absl::Cleanup cleanup = + [old_decorator = absl::debugging_internal::SetSymbolDecoratorFactory( + &TestSymbolDecorator<'a'>::Factory)] { + absl::debugging_internal::SetSymbolDecoratorFactory(old_decorator); + }; + + EXPECT_STREQ("nonstatic_func hello a", + TrySymbolize(reinterpret_cast<void*>(&nonstatic_func))); + + EXPECT_EQ(absl::debugging_internal::SetSymbolDecoratorFactory( + &TestSymbolDecorator<'b'>::Factory), + &TestSymbolDecorator<'a'>::Factory); + + EXPECT_STREQ("nonstatic_func hello b", + TrySymbolize(reinterpret_cast<void*>(&nonstatic_func))); +} + // 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
diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc index db24456..1ba5bf4 100644 --- a/absl/debugging/symbolize_unimplemented.inc +++ b/absl/debugging/symbolize_unimplemented.inc
@@ -14,16 +14,20 @@ #include <cstdint> -#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/internal/symbolize.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { -int InstallSymbolDecorator(SymbolDecorator, void*) { return -1; } +int InstallSymbolDecorator(LegacySymbolDecorator, void*) { return -1; } bool RemoveSymbolDecorator(int) { return false; } bool RemoveAllSymbolDecorators(void) { return false; } + +SymbolDecorator::Factory* SetSymbolDecoratorFactory(SymbolDecorator::Factory*) { + return nullptr; +} bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *) { return false; }
diff --git a/absl/status/internal/status_matchers.h b/absl/status/internal/status_matchers.h index eb61d73..2eafd13 100644 --- a/absl/status/internal/status_matchers.h +++ b/absl/status/internal/status_matchers.h
@@ -126,18 +126,24 @@ class StatusCode { public: /*implicit*/ StatusCode(int code) // NOLINT - : code_(static_cast<::absl::StatusCode>(code)) {} - /*implicit*/ StatusCode(::absl::StatusCode code) : code_(code) {} // NOLINT + : code_(code) {} + /*implicit*/ StatusCode(::absl::StatusCode code) // NOLINT + : code_(static_cast<int>(code)) {} explicit operator int() const { return static_cast<int>(code_); } friend inline void PrintTo(const StatusCode& code, std::ostream* os) { - // TODO(b/321095377): Change this to print the status code as a string. - *os << static_cast<int>(code); + absl::string_view text = + absl::StatusCodeToStringView(static_cast<absl::StatusCode>(code.code_)); + if (!text.empty()) { + *os << text; + } else { + *os << code.code_; + } } private: - ::absl::StatusCode code_; + int code_; }; // Relational operators to handle matchers like Eq, Lt, etc..
diff --git a/absl/status/status_matchers_test.cc b/absl/status/status_matchers_test.cc index 8656b2d..54cc733 100644 --- a/absl/status/status_matchers_test.cc +++ b/absl/status/status_matchers_test.cc
@@ -122,10 +122,9 @@ StatusIs(absl::StatusCode::kInvalidArgument, "ungueltig")); auto m = StatusIs(absl::StatusCode::kInternal, "internal error"); - EXPECT_THAT( - ::testing::DescribeMatcher<absl::Status>(m), - MatchesRegex( - "has a status code that .*, and has an error message that .*")); + EXPECT_THAT(::testing::DescribeMatcher<absl::Status>(m), + MatchesRegex("has a status code that is equal to INTERNAL, and " + "has an error message that .*")); EXPECT_THAT( ::testing::DescribeMatcher<absl::Status>(m, /*negation=*/true), MatchesRegex(
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h index 6281438..00e75de 100644 --- a/absl/time/internal/cctz/src/tzfile.h +++ b/absl/time/internal/cctz/src/tzfile.h
@@ -17,15 +17,8 @@ ** Thank you! */ -/* -** Information about time zone files. -*/ - -#ifndef TZDEFRULES -#define TZDEFRULES "posixrules" -#endif /* !defined TZDEFRULES */ - -/* See Internet RFC 9636 for more details about the following format. */ +/* Information about time zone files. + See Internet RFC 9636 for more details about the following format. */ /* ** Each file begins with. . . @@ -96,23 +89,25 @@ */ #ifndef TZ_MAX_TIMES -/* This must be at least 242 for Europe/London with 'zic -b fat'. */ +/* The following limit applies to localtime.c; zic has no such limit. + The limit must be at least 310 for Asia/Hebron with 'zic -b fat'. */ #define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES /* This must be at least 18 for Europe/Vilnius with 'zic -b fat'. */ -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#define TZ_MAX_TYPES 256 /* Limited to 256 by Internet RFC 9636. */ #endif /* !defined TZ_MAX_TYPES */ #ifndef TZ_MAX_CHARS /* This must be at least 40 for America/Anchorage. */ -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - /* (limited by what unsigned chars can hold) */ -#endif /* !defined TZ_MAX_CHARS */ +#define TZ_MAX_CHARS 256 /* Maximum number of abbreviation characters */ + /* (limited to 256 by Internet RFC 9636) */ +#endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS -/* This must be at least 27 for leap seconds from 1972 through mid-2023. +/* The following limit applies to localtime.c; zic has no such limit. + The limit must be at least 27 for leap seconds from 1972 through mid-2023. There's a plan to discontinue leap seconds by 2035. */ #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index cb3be9a..5d91260 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@ -2025c +2026a
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi index 166e434..247caa6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau index 9152e68..a60bddd 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol index 9152e68..a60bddd 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab index 1d64b39..aa3a64f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab +++ b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
@@ -166,9 +166,6 @@ # +02/+03 - EET/EEST (Lebanon DST) XX +3353+03530 Asia/Beirut Lebanon # -# +02/+03 - EET/EEST (Moldova DST) -XX +4700+02850 Europe/Chisinau Moldova -# # +02/+03 - EET/EEST (Palestine DST) XX +3130+03428 Asia/Gaza Palestine #
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index a52c358..19709a5 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel
@@ -51,6 +51,32 @@ ) cc_library( + name = "source_location", + hdrs = ["source_location.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + "//absl/base:nullability", + ], +) + +cc_test( + name = "source_location_test", + size = "small", + srcs = ["source_location_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":source_location", + "//absl/base:config", + "//absl/strings:string_view", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_library( name = "span", srcs = [ "internal/span.h",
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index 75dd07d..f69a1ea 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt
@@ -129,6 +129,32 @@ absl_cc_library( NAME + source_location + HDRS + "source_location.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::nullability + PUBLIC +) + +absl_cc_test( + NAME + source_location_test + SRCS + "source_location_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::source_location + GTest::gmock_main +) + +absl_cc_library( + NAME compare HDRS "compare.h"
diff --git a/absl/types/source_location.h b/absl/types/source_location.h new file mode 100644 index 0000000..6e14836 --- /dev/null +++ b/absl/types/source_location.h
@@ -0,0 +1,172 @@ +// Copyright 2017 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. +// +// ----------------------------------------------------------------------------- +// File: source_location.h +// ----------------------------------------------------------------------------- +// +// absl::SourceLocation provides source-code location info for C++17 and later. +// +// Critically, it is a **view type** (like std::string_view). Unlike +// std::source_location, it is not permanently valid and must not outlive its +// source. Using an invalid location is an error and may result in warnings +// or crashes. +// +// Additionally, it does not guarantee the retention of all caller information +// (e.g. column or function name) and may e.g. return unspecified values for +// performance reasons. +// +// To define a function that has access to the source location of the +// callsite, define it with a parameter of type `absl::SourceLocation`. The +// caller can then invoke the function, passing +// `absl::SourceLocation::current()` as the argument. +// +// If at all possible, make the `absl::SourceLocation` parameter be the +// function's last parameter. That way, when `std::source_location` is +// available, you will be able to switch to it, and give the parameter a default +// argument of `std::source_location::current()`. Users will then be able to +// omit that argument, and the default will automatically capture the location +// of the callsite. + +#ifndef ABSL_TYPES_SOURCE_LOCATION_H_ +#define ABSL_TYPES_SOURCE_LOCATION_H_ + +#include <cstdint> +#include <type_traits> + +#include "absl/base/config.h" +#include "absl/base/nullability.h" + +// This needs to come after absl/base/config.h, which is responsible for +// defining ABSL_HAVE_STD_SOURCE_LOCATION +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION +#include <source_location> // NOLINT(build/c++20) +#endif + +// For OSS release, whether to alias to std::source_location is configurable via +// config.h/options.h, similar to std::string_view/variant/etc. +#if defined(ABSL_USES_STD_SOURCE_LOCATION) && \ + defined(ABSL_HAVE_STD_SOURCE_LOCATION) +namespace absl { +ABSL_NAMESPACE_BEGIN +using SourceLocation = std::source_location; +ABSL_NAMESPACE_END +} // namespace absl + +#else // ABSL_HAVE_STD_SOURCE_LOCATION + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// C++17-compatible class representing a specific location in the source code of +// a program. Similar to std::source_location, but with a few key differences +// explained above. +class SourceLocation { + struct PrivateTag { + private: + explicit PrivateTag() = default; + friend class SourceLocation; + }; + + public: + // Avoid this constructor; it populates the object with dummy values. + SourceLocation() = default; + +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION + constexpr SourceLocation( // NOLINT(google-explicit-constructor) + std::source_location loc) + : SourceLocation(loc.line(), loc.file_name()) {} +#endif + +#ifdef ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE + // SourceLocation::current + // + // Creates a `SourceLocation` based on the current source location. Currently, + // it only captures file and line information for efficiency purposes, but + // that is subject to change. APIs that accept a `SourceLocation` as a default + // parameter can use this to capture their caller's locations. + // + // Example: + // + // void TracedAdd(int i, SourceLocation loc = SourceLocation::current()) { + // std::cout << loc.file_name() << ":" << loc.line() << " added " << i; + // ... + // } + // + // void UserCode() { + // TracedAdd(1); + // TracedAdd(2); + // } + static constexpr SourceLocation current( + PrivateTag = PrivateTag{}, std::uint_least32_t line = __builtin_LINE(), + const char* absl_nonnull file_name = __builtin_FILE()) { + return SourceLocation(line, file_name); + } +#else + // Creates a dummy `SourceLocation` of "<source_location>" at line number 1, + // if no `SourceLocation::current()` implementation is available. + static constexpr SourceLocation current() { + return SourceLocation(1, "<source_location>"); + } +#endif + // The line number of the captured source location, or an unspecified value + // if this information is not available. + constexpr std::uint_least32_t line() const noexcept { return line_; } + + // The column number of the captured source location, or an unspecified value + // if this information is not available. + constexpr std::uint_least32_t column() const noexcept { return 0; } + + // The file name of the captured source location, or an unspecified string + // if this information is not available. Guaranteed to never be NULL. + constexpr const char* absl_nonnull file_name() const noexcept { + return file_name_; + } + + // The function name of the captured source location, or an unspecified string + // if this information is not available. Guaranteed to never be NULL. + // + // NOTE: Currently, we deliberately avoid providing the function name, as it + // can bloat binary sizes and is non-critical. This may change in the future. + constexpr const char* absl_nonnull function_name() const noexcept { + return ""; + } + + private: + // `file_name` must outlive all copies of the `absl::SourceLocation` object, + // so in practice it should be a string literal. + constexpr SourceLocation(std::uint_least32_t line, + const char* absl_nonnull file_name) + : line_(line), file_name_(file_name) {} + + // We would use [[maybe_unused]] here, but it doesn't work on all supported + // toolchains at the moment. + friend constexpr int UseUnused() { + static_assert(SourceLocation(0, nullptr).unused_column_ == 0, + "Use the otherwise-unused member."); + return 0; + } + + // "unused" members are present to minimize future changes in the size of this + // type. + std::uint_least32_t line_ = 0; + std::uint_least32_t unused_column_ = 0; + const char* absl_nonnull file_name_ = ""; +}; + +ABSL_NAMESPACE_END +} // namespace absl +#endif // ABSL_HAVE_STD_SOURCE_LOCATION + +#endif // ABSL_TYPES_SOURCE_LOCATION_H_
diff --git a/absl/types/source_location_test.cc b/absl/types/source_location_test.cc new file mode 100644 index 0000000..a312aa9 --- /dev/null +++ b/absl/types/source_location_test.cc
@@ -0,0 +1,139 @@ +// Copyright 2017 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/types/source_location.h" + +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION +#include <source_location> // NOLINT(build/c++20) +#endif +#include <type_traits> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" + +namespace { + +using ::absl::SourceLocation; +using ::testing::EndsWith; + +TEST(SourceLocationTest, DefaultConstructionWorks) { + static_assert(!std::is_trivially_default_constructible_v<SourceLocation>); + static_assert(std::is_nothrow_default_constructible_v<SourceLocation>); + constexpr SourceLocation loc1 [[maybe_unused]]; + SourceLocation loc2 [[maybe_unused]]{}; + EXPECT_EQ(loc1.line(), loc2.line()); +} + +#ifdef ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE + +TEST(SourceLocationTest, ConstexprMembers) { + constexpr SourceLocation loc1 = absl::SourceLocation::current(); + const SourceLocation loc2 = absl::SourceLocation::current(); + EXPECT_EQ(loc1.line(), loc2.line() - 1); + EXPECT_EQ(absl::string_view(loc1.file_name()), loc2.file_name()); +} + +TEST(SourceLocationTest, ConversionFromStdSourceLocationWorks) { +#ifndef ABSL_HAVE_STD_SOURCE_LOCATION + GTEST_SKIP() << "std::source_location is not available"; +#else + constexpr SourceLocation loc1 = std::source_location::current(); + const std::source_location loc2 = std::source_location::current(); + EXPECT_EQ(loc1.line(), loc2.line() - 1); + EXPECT_EQ(absl::string_view(loc1.file_name()), loc2.file_name()); +#endif +} + +TEST(SourceLocationTest, CopyConstructionWorks) { + constexpr SourceLocation location = absl::SourceLocation::current(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +TEST(SourceLocationTest, CopyAssignmentWorks) { + SourceLocation location; + location = absl::SourceLocation::current(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +SourceLocation Echo(const SourceLocation& location) { return location; } + +TEST(SourceLocationTest, ExpectedUsageWorks) { + SourceLocation location = Echo(absl::SourceLocation::current()); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +TEST(SourceLocationTest, CurrentWorks) { + constexpr SourceLocation location = SourceLocation::current(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +SourceLocation FuncWithDefaultParam( + SourceLocation loc = SourceLocation::current()) { + return loc; +} + +TEST(SourceLocationTest, CurrentWorksAsDefaultParam) { + SourceLocation location = FuncWithDefaultParam(); + constexpr int line = __LINE__ - 1; + + EXPECT_EQ(location.line(), line); + EXPECT_THAT(location.file_name(), EndsWith("source_location_test.cc")); +} + +#endif + +template <typename T> +bool TryPassLineAndFile(decltype(T::current(0, ""))*) { + return true; +} +template <typename T> +bool TryPassLineAndFile(decltype(T::current({}, 0, ""))*) { + return true; +} +template <typename T> +bool TryPassLineAndFile(decltype(T::current(typename T::Tag{}, 0, ""))*) { + return true; +} +template <typename T> +bool TryPassLineAndFile(...) { + return false; +} + +TEST(SourceLocationTest, CantPassLineAndFile) { +#ifdef ABSL_HAVE_STD_SOURCE_LOCATION + using StdSourceLocation = std::source_location; +#else + using StdSourceLocation = void; +#endif + if constexpr (!std::is_same_v<absl::SourceLocation, StdSourceLocation>) { + EXPECT_FALSE(TryPassLineAndFile<absl::SourceLocation>(nullptr)); + } +} + +} // namespace
diff --git a/ci/absl_alternate_options.h b/ci/absl_alternate_options.h index d5567f3..7f70aad 100644 --- a/ci/absl_alternate_options.h +++ b/ci/absl_alternate_options.h
@@ -20,6 +20,7 @@ #ifndef ABSL_CI_ABSL_ALTERNATE_OPTIONS_H_ #define ABSL_CI_ABSL_ALTERNATE_OPTIONS_H_ +#define ABSL_OPTION_USE_STD_SOURCE_LOCATION 0 #define ABSL_OPTION_USE_STD_ORDERING 0 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns