Commit 5b535401 by Abseil Team Committed by Jon Cohen

- ed0ba496fe01eb8edfa86beade8a37768e7c12ef Updates the API for Exception…

  - ed0ba496fe01eb8edfa86beade8a37768e7c12ef Updates the API for Exception Safety testing to use build... by Abseil Team <absl-team@google.com>
  - c4b7a4e517c9404932c45f2f9f92eb7dc694e45d Internal change by Abseil Team <absl-team@google.com>
  - 76c78ed9385f65d881511645446e0bb8ababf6ec Add missing ABSL_PREDICT_FALSE to one of FixedArray::at()... by Abseil Team <absl-team@google.com>
  - 1204fb1c46f007dd9dfb7d9abf3e96c58835d193 Internal change. by Greg Falcon <gfalcon@google.com>
  - f1f47c98a026bc5e425ae83ff4a2eb391bbd3d9b Add internal-only functionality to examine the stack, to ... by Derek Mauro <dmauro@google.com>
  - 30d63097cd268d912f917526f6511005580465c4 fix typo by Abseil Team <absl-team@google.com>
  - 942d7efa6cf54cd248ca57dcaf3c245188b52a76 Remove unnecessary semicolons from comment examples. by Abseil Team <absl-team@google.com>
  - 7db0669cf23a06d934d3ed8c76aee4e4e23b7e04 Remove malloc_hook and malloc_extension from our internal... by Greg Falcon <gfalcon@google.com>
  - 0190f1063d101b1ded355019df2e1d325931f6c7 Make the maximum length of a string view equal difference... by Abseil Team <absl-team@google.com>
  - c8ae37cbce29449b02115a0ebd435ddc3d7ab062 Add namespace qualification. by Shaindel Schwartz <shaindel@google.com>
  - ff70afe2e6e3dd39f51ce9829e3e1f18231bf4d7 Fix internal/direct_mmap.h for non-linux builds. by Greg Falcon <gfalcon@google.com>

GitOrigin-RevId: ed0ba496fe01eb8edfa86beade8a37768e7c12ef
Change-Id: I7595ee3480d1d6724fd3797c15ba9d9be0d17e62
parent a7e522da
...@@ -78,41 +78,15 @@ cc_library( ...@@ -78,41 +78,15 @@ cc_library(
) )
cc_library( cc_library(
name = "malloc_extension",
srcs = ["internal/malloc_extension.cc"],
hdrs = [
"internal/malloc_extension.h",
],
copts = ABSL_DEFAULT_COPTS,
visibility = [
"//absl:__subpackages__",
],
deps = [
":core_headers",
":dynamic_annotations",
],
)
# malloc_extension feels like it wants to be folded into this target, but
# malloc_internal gets special build treatment to compile at -O3, so these
# need to stay separate.
cc_library(
name = "malloc_internal", name = "malloc_internal",
srcs = [ srcs = [
"internal/low_level_alloc.cc", "internal/low_level_alloc.cc",
"internal/malloc_hook.cc",
"internal/malloc_hook_mmap_linux.inc",
], ],
hdrs = [ hdrs = [
"internal/direct_mmap.h", "internal/direct_mmap.h",
"internal/low_level_alloc.h", "internal/low_level_alloc.h",
"internal/malloc_hook.h",
"internal/malloc_hook_c.h",
], ],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
textual_hdrs = [
"internal/malloc_hook_invoke.h",
],
visibility = [ visibility = [
"//absl:__subpackages__", "//absl:__subpackages__",
], ],
...@@ -419,26 +393,3 @@ cc_test( ...@@ -419,26 +393,3 @@ cc_test(
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],
) )
cc_test(
name = "malloc_extension_system_malloc_test",
size = "small",
srcs = ["internal/malloc_extension_test.cc"],
copts = select({
"//absl:windows": [
"/DABSL_MALLOC_EXTENSION_TEST_ALLOW_MISSING_EXTENSION=1",
],
"//conditions:default": [
"-DABSL_MALLOC_EXTENSION_TEST_ALLOW_MISSING_EXTENSION=1",
],
}) + ABSL_TEST_COPTS,
features = [
# This test can't be run under lsan because the test requires system
# malloc, and lsan provides a competing malloc implementation.
"-leak_sanitize",
],
deps = [
":malloc_extension",
"@com_google_googletest//:gtest_main",
],
)
...@@ -41,10 +41,6 @@ list(APPEND BASE_INTERNAL_HEADERS ...@@ -41,10 +41,6 @@ list(APPEND BASE_INTERNAL_HEADERS
"internal/inline_variable.h" "internal/inline_variable.h"
"internal/low_level_alloc.h" "internal/low_level_alloc.h"
"internal/low_level_scheduling.h" "internal/low_level_scheduling.h"
"internal/malloc_extension.h"
"internal/malloc_hook_c.h"
"internal/malloc_hook.h"
"internal/malloc_hook_invoke.h"
"internal/per_thread_tls.h" "internal/per_thread_tls.h"
"internal/pretty_function.h" "internal/pretty_function.h"
"internal/raw_logging.h" "internal/raw_logging.h"
...@@ -69,7 +65,6 @@ list(APPEND BASE_SRC ...@@ -69,7 +65,6 @@ list(APPEND BASE_SRC
"internal/thread_identity.cc" "internal/thread_identity.cc"
"internal/unscaledcycleclock.cc" "internal/unscaledcycleclock.cc"
"internal/low_level_alloc.cc" "internal/low_level_alloc.cc"
"internal/malloc_hook.cc"
${BASE_PUBLIC_HEADERS} ${BASE_PUBLIC_HEADERS}
${BASE_INTERNAL_HEADERS} ${BASE_INTERNAL_HEADERS}
) )
...@@ -86,21 +81,6 @@ absl_library( ...@@ -86,21 +81,6 @@ absl_library(
base base
) )
# malloc extension library
set(MALLOC_EXTENSION_SRC "internal/malloc_extension.cc")
set(MALLOC_EXTENSION_PUBLIC_LIBRARIES absl::base)
absl_library(
TARGET
absl_malloc_extension
SOURCES
${MALLOC_EXTENSION_SRC}
PUBLIC_LIBRARIES
${MALLOC_EXTENSION_PUBLIC_LIBRARIES}
EXPORT_NAME
malloc_extension
)
# throw delegate library # throw delegate library
set(THROW_DELEGATE_SRC "internal/throw_delegate.cc") set(THROW_DELEGATE_SRC "internal/throw_delegate.cc")
...@@ -165,8 +145,6 @@ absl_library( ...@@ -165,8 +145,6 @@ absl_library(
# malloc_internal library # malloc_internal library
list(APPEND MALLOC_INTERNAL_SRC list(APPEND MALLOC_INTERNAL_SRC
"internal/low_level_alloc.cc" "internal/low_level_alloc.cc"
"internal/malloc_hook.cc"
"internal/malloc_hook_mmap_linux.inc"
) )
absl_library( absl_library(
...@@ -378,23 +356,3 @@ absl_test( ...@@ -378,23 +356,3 @@ absl_test(
PRIVATE_COMPILE_FLAGS PRIVATE_COMPILE_FLAGS
${ABSL_EXCEPTIONS_FLAG} ${ABSL_EXCEPTIONS_FLAG}
) )
# test absl_malloc_extension_system_malloc_test
set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_SRC "internal/malloc_extension_test.cc")
set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PUBLIC_LIBRARIES absl::base absl_malloc_extension)
set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PRIVATE_COMPILE_FLAGS "-DABSL_MALLOC_EXTENSION_TEST_ALLOW_MISSING_EXTENSION=1")
absl_test(
TARGET
absl_malloc_extension_system_malloc_test
SOURCES
${MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_SRC}
PUBLIC_LIBRARIES
${MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PUBLIC_LIBRARIES}
PRIVATE_COMPILE_FLAGS
${MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PRIVATE_COMPILE_FLAGS}
)
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
// This header file defines casting templates to fit use cases not covered by // This header file defines casting templates to fit use cases not covered by
// the standard casts provided in the C++ standard. As with all cast operations, // the standard casts provided in the C++ standard. As with all cast operations,
// use these with caution and only if alternatives do not exist. // use these with caution and only if alternatives do not exist.
//
#ifndef ABSL_BASE_CASTS_H_ #ifndef ABSL_BASE_CASTS_H_
#define ABSL_BASE_CASTS_H_ #define ABSL_BASE_CASTS_H_
...@@ -122,7 +121,7 @@ inline To implicit_cast(typename absl::internal::identity_t<To> to) { ...@@ -122,7 +121,7 @@ inline To implicit_cast(typename absl::internal::identity_t<To> to) {
// object in memory has one type, and a program accesses it with a different // object in memory has one type, and a program accesses it with a different
// type, the result is undefined behavior for most values of "different type". // type, the result is undefined behavior for most values of "different type".
// //
// Such casting results is type punning: holding an object in memory of one type // Such casting results in type punning: holding an object in memory of one type
// and reading its bits back using a different type. A `bit_cast()` avoids this // and reading its bits back using a different type. A `bit_cast()` avoids this
// issue by implementating its casts using `memcpy()`, which avoids introducing // issue by implementating its casts using `memcpy()`, which avoids introducing
// this undefined behavior. // this undefined behavior.
......
...@@ -67,7 +67,7 @@ namespace base_internal { ...@@ -67,7 +67,7 @@ namespace base_internal {
// Platform specific logic extracted from // Platform specific logic extracted from
// https://chromium.googlesource.com/linux-syscall-support/+/master/linux_syscall_support.h // https://chromium.googlesource.com/linux-syscall-support/+/master/linux_syscall_support.h
inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
off64_t offset) __THROW { off64_t offset) noexcept {
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
(defined(__PPC__) && !defined(__PPC64__)) || \ (defined(__PPC__) && !defined(__PPC64__)) || \
...@@ -129,6 +129,9 @@ inline int DirectMunmap(void* start, size_t length) { ...@@ -129,6 +129,9 @@ inline int DirectMunmap(void* start, size_t length) {
// For non-linux platforms where we have mmap, just dispatch directly to the // For non-linux platforms where we have mmap, just dispatch directly to the
// actual mmap()/munmap() methods. // actual mmap()/munmap() methods.
namespace absl {
namespace base_internal {
inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
off_t offset) { off_t offset) {
return mmap(start, length, prot, flags, fd, offset); return mmap(start, length, prot, flags, fd, offset);
...@@ -138,6 +141,9 @@ inline int DirectMunmap(void* start, size_t length) { ...@@ -138,6 +141,9 @@ inline int DirectMunmap(void* start, size_t length) {
return munmap(start, length); return munmap(start, length);
} }
} // namespace base_internal
} // namespace absl
#endif // __linux__ #endif // __linux__
#endif // ABSL_HAVE_MMAP #endif // ABSL_HAVE_MMAP
......
...@@ -18,7 +18,10 @@ ...@@ -18,7 +18,10 @@
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
namespace absl { namespace absl {
exceptions_internal::NoThrowTag no_throw_ctor; exceptions_internal::NoThrowTag no_throw_ctor;
exceptions_internal::StrongGuaranteeTagType strong_guarantee;
namespace exceptions_internal { namespace exceptions_internal {
int countdown = -1; int countdown = -1;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "absl/base/call_once.h" #include "absl/base/call_once.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/direct_mmap.h"
#include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/scheduling_mode.h"
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/base/thread_annotations.h" #include "absl/base/thread_annotations.h"
...@@ -49,8 +50,6 @@ ...@@ -49,8 +50,6 @@
#include <new> // for placement-new #include <new> // for placement-new
#include "absl/base/dynamic_annotations.h" #include "absl/base/dynamic_annotations.h"
#include "absl/base/internal/malloc_hook.h"
#include "absl/base/internal/malloc_hook_invoke.h"
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/spinlock.h" #include "absl/base/internal/spinlock.h"
...@@ -405,7 +404,7 @@ bool LowLevelAlloc::DeleteArena(Arena *arena) { ...@@ -405,7 +404,7 @@ bool LowLevelAlloc::DeleteArena(Arena *arena) {
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) { if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
munmap_result = munmap(region, size); munmap_result = munmap(region, size);
} else { } else {
munmap_result = MallocHook::UnhookedMUnmap(region, size); munmap_result = base_internal::DirectMunmap(region, size);
} }
if (munmap_result != 0) { if (munmap_result != 0) {
ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d", ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d",
...@@ -503,9 +502,6 @@ void LowLevelAlloc::Free(void *v) { ...@@ -503,9 +502,6 @@ void LowLevelAlloc::Free(void *v) {
ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header), ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
"bad magic number in Free()"); "bad magic number in Free()");
LowLevelAlloc::Arena *arena = f->header.arena; LowLevelAlloc::Arena *arena = f->header.arena;
if ((arena->flags & kCallMallocHook) != 0) {
MallocHook::InvokeDeleteHook(v);
}
ArenaLock section(arena); ArenaLock section(arena);
AddToFreelist(v, arena); AddToFreelist(v, arena);
ABSL_RAW_CHECK(arena->allocation_count > 0, "nothing in arena to free"); ABSL_RAW_CHECK(arena->allocation_count > 0, "nothing in arena to free");
...@@ -550,7 +546,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { ...@@ -550,7 +546,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed"); ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed");
#else #else
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
new_pages = MallocHook::UnhookedMMap(nullptr, new_pages_size, new_pages = base_internal::DirectMmap(nullptr, new_pages_size,
PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
} else { } else {
new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ, new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
...@@ -593,21 +589,12 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { ...@@ -593,21 +589,12 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
void *LowLevelAlloc::Alloc(size_t request) { void *LowLevelAlloc::Alloc(size_t request) {
void *result = DoAllocWithArena(request, DefaultArena()); void *result = DoAllocWithArena(request, DefaultArena());
// The default arena always calls the malloc hook.
// This call must be directly in the user-called allocator function
// for MallocHook::GetCallerStackTrace to work properly
MallocHook::InvokeNewHook(result, request);
return result; return result;
} }
void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) {
ABSL_RAW_CHECK(arena != nullptr, "must pass a valid arena"); ABSL_RAW_CHECK(arena != nullptr, "must pass a valid arena");
void *result = DoAllocWithArena(request, arena); void *result = DoAllocWithArena(request, arena);
if ((arena->flags & kCallMallocHook) != 0) {
// this call must be directly in the user-called allocator function
// for MallocHook::GetCallerStackTrace to work properly
MallocHook::InvokeNewHook(result, request);
}
return result; return result;
} }
......
...@@ -21,8 +21,6 @@ ...@@ -21,8 +21,6 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include "absl/base/internal/malloc_hook.h"
namespace absl { namespace absl {
namespace base_internal { namespace base_internal {
namespace { namespace {
...@@ -139,58 +137,12 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) { ...@@ -139,58 +137,12 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) {
TEST_ASSERT(LowLevelAlloc::DeleteArena(arena)); TEST_ASSERT(LowLevelAlloc::DeleteArena(arena));
} }
} }
// used for counting allocates and frees
static int32_t allocates;
static int32_t frees;
// ignore uses of the allocator not triggered by our test
static std::thread::id* test_tid;
// called on each alloc if kCallMallocHook specified
static void AllocHook(const void *p, size_t size) {
if (using_low_level_alloc) {
if (*test_tid == std::this_thread::get_id()) {
allocates++;
}
}
}
// called on each free if kCallMallocHook specified
static void FreeHook(const void *p) {
if (using_low_level_alloc) {
if (*test_tid == std::this_thread::get_id()) {
frees++;
}
}
}
// LowLevelAlloc is designed to be safe to call before main(). // LowLevelAlloc is designed to be safe to call before main().
static struct BeforeMain { static struct BeforeMain {
BeforeMain() { BeforeMain() {
test_tid = new std::thread::id(std::this_thread::get_id());
TEST_ASSERT(MallocHook::AddNewHook(&AllocHook));
TEST_ASSERT(MallocHook::AddDeleteHook(&FreeHook));
TEST_ASSERT(allocates == 0);
TEST_ASSERT(frees == 0);
Test(false, false, 50000); Test(false, false, 50000);
TEST_ASSERT(allocates != 0); // default arena calls hooks Test(true, false, 50000);
TEST_ASSERT(frees != 0); Test(true, true, 50000);
for (int i = 0; i != 16; i++) {
bool call_hooks = ((i & 1) == 1);
allocates = 0;
frees = 0;
Test(true, call_hooks, 15000);
if (call_hooks) {
TEST_ASSERT(allocates > 5000); // arena calls hooks
TEST_ASSERT(frees > 5000);
} else {
TEST_ASSERT(allocates == 0); // arena doesn't call hooks
TEST_ASSERT(frees == 0);
}
}
TEST_ASSERT(MallocHook::RemoveNewHook(&AllocHook));
TEST_ASSERT(MallocHook::RemoveDeleteHook(&FreeHook));
} }
} before_main; } before_main;
......
// 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
//
// 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.
#include "absl/base/internal/malloc_extension.h"
#include <assert.h>
#include <string.h>
#include <atomic>
#include <string>
#include "absl/base/dynamic_annotations.h"
namespace absl {
namespace base_internal {
// SysAllocator implementation
SysAllocator::~SysAllocator() {}
void SysAllocator::GetStats(char* buffer, int) { buffer[0] = 0; }
// Dummy key method to avoid weak vtable.
void MallocExtensionWriter::UnusedKeyMethod() {}
void StringMallocExtensionWriter::Write(const char* buf, int len) {
out_->append(buf, len);
}
// Default implementation -- does nothing
MallocExtension::~MallocExtension() { }
bool MallocExtension::VerifyAllMemory() { return true; }
bool MallocExtension::VerifyNewMemory(const void*) { return true; }
bool MallocExtension::VerifyArrayNewMemory(const void*) { return true; }
bool MallocExtension::VerifyMallocMemory(const void*) { return true; }
bool MallocExtension::GetNumericProperty(const char*, size_t*) {
return false;
}
bool MallocExtension::SetNumericProperty(const char*, size_t) {
return false;
}
void MallocExtension::GetStats(char* buffer, int length) {
assert(length > 0);
static_cast<void>(length);
buffer[0] = '\0';
}
bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total,
int histogram[kMallocHistogramSize]) {
*blocks = 0;
*total = 0;
memset(histogram, 0, sizeof(*histogram) * kMallocHistogramSize);
return true;
}
void MallocExtension::MarkThreadIdle() {
// Default implementation does nothing
}
void MallocExtension::MarkThreadBusy() {
// Default implementation does nothing
}
SysAllocator* MallocExtension::GetSystemAllocator() {
return nullptr;
}
void MallocExtension::SetSystemAllocator(SysAllocator*) {
// Default implementation does nothing
}
void MallocExtension::ReleaseToSystem(size_t) {
// Default implementation does nothing
}
void MallocExtension::ReleaseFreeMemory() {
ReleaseToSystem(static_cast<size_t>(-1)); // SIZE_T_MAX
}
void MallocExtension::SetMemoryReleaseRate(double) {
// Default implementation does nothing
}
double MallocExtension::GetMemoryReleaseRate() {
return -1.0;
}
size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) {
return size;
}
size_t MallocExtension::GetAllocatedSize(const void* p) {
assert(GetOwnership(p) != kNotOwned);
static_cast<void>(p);
return 0;
}
MallocExtension::Ownership MallocExtension::GetOwnership(const void*) {
return kUnknownOwnership;
}
void MallocExtension::GetProperties(MallocExtension::StatLevel,
std::map<std::string, Property>* result) {
result->clear();
}
size_t MallocExtension::ReleaseCPUMemory(int) {
return 0;
}
// The current malloc extension object.
std::atomic<MallocExtension*> MallocExtension::current_instance_;
MallocExtension* MallocExtension::InitModule() {
MallocExtension* ext = new MallocExtension;
current_instance_.store(ext, std::memory_order_release);
return ext;
}
void MallocExtension::Register(MallocExtension* implementation) {
InitModuleOnce();
// When running under valgrind, our custom malloc is replaced with
// valgrind's one and malloc extensions will not work. (Note:
// callers should be responsible for checking that they are the
// malloc that is really being run, before calling Register. This
// is just here as an extra sanity check.)
// Under compiler-based ThreadSanitizer RunningOnValgrind() returns true,
// but we still want to use malloc extensions.
#ifndef THREAD_SANITIZER
if (RunningOnValgrind()) {
return;
}
#endif // #ifndef THREAD_SANITIZER
current_instance_.store(implementation, std::memory_order_release);
}
void MallocExtension::GetHeapSample(MallocExtensionWriter*) {}
void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter*) {}
void MallocExtension::GetFragmentationProfile(MallocExtensionWriter*) {}
} // namespace base_internal
} // namespace absl
// Default implementation just returns size. The expectation is that
// the linked-in malloc implementation might provide an override of
// this weak function with a better implementation.
ABSL_ATTRIBUTE_WEAK ABSL_ATTRIBUTE_NOINLINE size_t nallocx(size_t size, int) {
return size;
}
/*
* 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
*
* 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.
* C shims for the C++ malloc_extension.h. See malloc_extension.h for
* details. Note these C shims always work on
* MallocExtension::instance(); it is not possible to have more than
* one MallocExtension object in C applications.
*/
#ifndef ABSL_BASE_INTERNAL_MALLOC_EXTENSION_C_H_
#define ABSL_BASE_INTERNAL_MALLOC_EXTENSION_C_H_
#include <stddef.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
#define kMallocExtensionHistogramSize 64
int MallocExtension_VerifyAllMemory(void);
int MallocExtension_VerifyNewMemory(const void* p);
int MallocExtension_VerifyArrayNewMemory(const void* p);
int MallocExtension_VerifyMallocMemory(const void* p);
int MallocExtension_MallocMemoryStats(int* blocks, size_t* total,
int histogram[kMallocExtensionHistogramSize]);
void MallocExtension_GetStats(char* buffer, int buffer_length);
/* TODO(csilvers): write a C version of these routines, that perhaps
* takes a function ptr and a void *.
*/
/* void MallocExtension_GetHeapSample(MallocExtensionWriter* result); */
/* void MallocExtension_GetHeapGrowthStacks(MallocExtensionWriter* result); */
int MallocExtension_GetNumericProperty(const char* property, size_t* value);
int MallocExtension_SetNumericProperty(const char* property, size_t value);
void MallocExtension_MarkThreadIdle(void);
void MallocExtension_MarkThreadBusy(void);
void MallocExtension_ReleaseToSystem(size_t num_bytes);
void MallocExtension_ReleaseFreeMemory(void);
size_t MallocExtension_GetEstimatedAllocatedSize(size_t size);
size_t MallocExtension_GetAllocatedSize(const void* p);
/*
* NOTE: These enum values MUST be kept in sync with the version in
* malloc_extension.h
*/
typedef enum {
MallocExtension_kUnknownOwnership = 0,
MallocExtension_kOwned,
MallocExtension_kNotOwned
} MallocExtension_Ownership;
MallocExtension_Ownership MallocExtension_GetOwnership(const void* p);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* ABSL_BASE_INTERNAL_MALLOC_EXTENSION_C_H_ */
/*
* 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
*
* 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.
*/
#include <algorithm>
#include <cstdlib>
#include "gtest/gtest.h"
#include "absl/base/internal/malloc_extension.h"
namespace absl {
namespace base_internal {
namespace {
TEST(MallocExtension, MallocExtension) {
void* a = malloc(1000);
size_t cxx_bytes_used, c_bytes_used;
if (!MallocExtension::instance()->GetNumericProperty(
"generic.current_allocated_bytes", &cxx_bytes_used)) {
EXPECT_TRUE(ABSL_MALLOC_EXTENSION_TEST_ALLOW_MISSING_EXTENSION);
} else {
ASSERT_TRUE(MallocExtension::instance()->GetNumericProperty(
"generic.current_allocated_bytes", &cxx_bytes_used));
#ifndef MEMORY_SANITIZER
EXPECT_GT(cxx_bytes_used, 1000);
EXPECT_GT(c_bytes_used, 1000);
#endif
EXPECT_TRUE(MallocExtension::instance()->VerifyAllMemory());
EXPECT_EQ(MallocExtension::kOwned,
MallocExtension::instance()->GetOwnership(a));
// TODO(csilvers): this relies on undocumented behavior that
// GetOwnership works on stack-allocated variables. Use a better test.
EXPECT_EQ(MallocExtension::kNotOwned,
MallocExtension::instance()->GetOwnership(&cxx_bytes_used));
EXPECT_EQ(MallocExtension::kNotOwned,
MallocExtension::instance()->GetOwnership(nullptr));
EXPECT_GE(MallocExtension::instance()->GetAllocatedSize(a), 1000);
// This is just a sanity check. If we allocated too much, tcmalloc is
// broken
EXPECT_LE(MallocExtension::instance()->GetAllocatedSize(a), 5000);
EXPECT_GE(MallocExtension::instance()->GetEstimatedAllocatedSize(1000),
1000);
for (int i = 0; i < 10; ++i) {
void* p = malloc(i);
EXPECT_GE(MallocExtension::instance()->GetAllocatedSize(p),
MallocExtension::instance()->GetEstimatedAllocatedSize(i));
free(p);
}
}
free(a);
}
TEST(nallocx, SaneBehavior) {
for (size_t size = 0; size < 64 * 1024; ++size) {
size_t alloc_size = nallocx(size, 0);
EXPECT_LE(size, alloc_size) << "size is " << size;
EXPECT_LE(alloc_size, std::max(size + 100, 2 * size)) << "size is " << size;
}
}
} // namespace
} // namespace base_internal
} // namespace absl
/*
* 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
*
* 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.
*/
/*
* C shims for the C++ malloc_hook.h. See malloc_hook.h for details
* on how to use these.
*/
#ifndef ABSL_BASE_INTERNAL_MALLOC_HOOK_C_H_
#define ABSL_BASE_INTERNAL_MALLOC_HOOK_C_H_
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef int (*MallocHook_GetStackTraceFn)(void**, int, int);
typedef void (*MallocHook_NewHook)(const void* ptr, size_t size);
typedef void (*MallocHook_DeleteHook)(const void* ptr);
typedef int64_t MallocHook_AllocHandle;
typedef struct {
/* See malloc_hook.h for documentation for this struct. */
MallocHook_AllocHandle handle;
size_t allocated_size;
double weight;
int stack_depth;
const void* stack;
} MallocHook_SampledAlloc;
typedef void (*MallocHook_SampledNewHook)(
const MallocHook_SampledAlloc* sampled_alloc);
typedef void (*MallocHook_SampledDeleteHook)(MallocHook_AllocHandle handle);
typedef void (*MallocHook_PreMmapHook)(const void* start, size_t size,
int protection, int flags, int fd,
off_t offset);
typedef void (*MallocHook_MmapHook)(const void* result, const void* start,
size_t size, int protection, int flags,
int fd, off_t offset);
typedef int (*MallocHook_MmapReplacement)(const void* start, size_t size,
int protection, int flags, int fd,
off_t offset, void** result);
typedef void (*MallocHook_MunmapHook)(const void* start, size_t size);
typedef int (*MallocHook_MunmapReplacement)(const void* start, size_t size,
int* result);
typedef void (*MallocHook_MremapHook)(const void* result, const void* old_addr,
size_t old_size, size_t new_size,
int flags, const void* new_addr);
typedef void (*MallocHook_PreSbrkHook)(ptrdiff_t increment);
typedef void (*MallocHook_SbrkHook)(const void* result, ptrdiff_t increment);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* ABSL_BASE_INTERNAL_MALLOC_HOOK_C_H_ */
//
// 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
//
// 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.
///
// This has the implementation details of malloc_hook that are needed
// to use malloc-hook inside the tcmalloc system. It does not hold
// any of the client-facing calls that are used to add new hooks.
//
// IWYU pragma: private, include "base/malloc_hook-inl.h"
#ifndef ABSL_BASE_INTERNAL_MALLOC_HOOK_INVOKE_H_
#define ABSL_BASE_INTERNAL_MALLOC_HOOK_INVOKE_H_
#include <sys/types.h>
#include <atomic>
#include <cstddef>
#include "absl/base/internal/malloc_hook.h"
namespace absl {
namespace base_internal {
// Maximum of 7 hooks means that HookList is 8 words.
static constexpr int kHookListMaxValues = 7;
// HookList: a class that provides synchronized insertions and removals and
// lockless traversal. Most of the implementation is in malloc_hook.cc.
template <typename T>
struct HookList {
static_assert(sizeof(T) <= sizeof(intptr_t), "T_should_fit_in_intptr_t");
// Adds value to the list. Note that duplicates are allowed. Thread-safe and
// blocking (acquires hooklist_spinlock). Returns true on success; false
// otherwise (failures include invalid value and no space left).
bool Add(T value);
// Removes the first entry matching value from the list. Thread-safe and
// blocking (acquires hooklist_spinlock). Returns true on success; false
// otherwise (failures include invalid value and no value found).
bool Remove(T value);
// Store up to n values of the list in output_array, and return the number of
// elements stored. Thread-safe and non-blocking. This is fast (one memory
// access) if the list is empty.
int Traverse(T* output_array, int n) const;
// Fast inline implementation for fast path of Invoke*Hook.
bool empty() const {
// empty() is only used as an optimization to determine if we should call
// Traverse which has proper acquire loads. Memory reordering around a
// call to empty will either lead to an unnecessary Traverse call, or will
// miss invoking hooks, neither of which is a problem.
return priv_end.load(std::memory_order_relaxed) == 0;
}
// This internal data is not private so that the class is an aggregate and can
// be initialized by the linker. Don't access this directly. Use the
// INIT_HOOK_LIST macro in malloc_hook.cc.
// One more than the index of the last valid element in priv_data. During
// 'Remove' this may be past the last valid element in priv_data, but
// subsequent values will be 0.
std::atomic<int> priv_end;
std::atomic<intptr_t> priv_data[kHookListMaxValues];
};
extern template struct HookList<MallocHook::NewHook>;
extern HookList<MallocHook::NewHook> new_hooks_;
extern HookList<MallocHook::DeleteHook> delete_hooks_;
extern HookList<MallocHook::SampledNewHook> sampled_new_hooks_;
extern HookList<MallocHook::SampledDeleteHook> sampled_delete_hooks_;
extern HookList<MallocHook::PreMmapHook> premmap_hooks_;
extern HookList<MallocHook::MmapHook> mmap_hooks_;
extern HookList<MallocHook::MmapReplacement> mmap_replacement_;
extern HookList<MallocHook::MunmapHook> munmap_hooks_;
extern HookList<MallocHook::MunmapReplacement> munmap_replacement_;
extern HookList<MallocHook::MremapHook> mremap_hooks_;
extern HookList<MallocHook::PreSbrkHook> presbrk_hooks_;
extern HookList<MallocHook::SbrkHook> sbrk_hooks_;
inline void MallocHook::InvokeNewHook(const void* ptr, size_t size) {
if (!absl::base_internal::new_hooks_.empty()) {
InvokeNewHookSlow(ptr, size);
}
}
inline void MallocHook::InvokeDeleteHook(const void* ptr) {
if (!absl::base_internal::delete_hooks_.empty()) {
InvokeDeleteHookSlow(ptr);
}
}
inline void MallocHook::InvokeSampledNewHook(
const SampledAlloc* sampled_alloc) {
if (!absl::base_internal::sampled_new_hooks_.empty()) {
InvokeSampledNewHookSlow(sampled_alloc);
}
}
inline void MallocHook::InvokeSampledDeleteHook(AllocHandle handle) {
if (!absl::base_internal::sampled_delete_hooks_.empty()) {
InvokeSampledDeleteHookSlow(handle);
}
}
inline void MallocHook::InvokePreMmapHook(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset) {
if (!absl::base_internal::premmap_hooks_.empty()) {
InvokePreMmapHookSlow(start, size, protection, flags, fd, offset);
}
}
inline void MallocHook::InvokeMmapHook(const void* result,
const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset) {
if (!absl::base_internal::mmap_hooks_.empty()) {
InvokeMmapHookSlow(result, start, size, protection, flags, fd, offset);
}
}
inline bool MallocHook::InvokeMmapReplacement(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset,
void** result) {
if (!absl::base_internal::mmap_replacement_.empty()) {
return InvokeMmapReplacementSlow(start, size,
protection, flags,
fd, offset,
result);
}
return false;
}
inline void MallocHook::InvokeMunmapHook(const void* start, size_t size) {
if (!absl::base_internal::munmap_hooks_.empty()) {
InvokeMunmapHookSlow(start, size);
}
}
inline bool MallocHook::InvokeMunmapReplacement(
const void* start, size_t size, int* result) {
if (!absl::base_internal::mmap_replacement_.empty()) {
return InvokeMunmapReplacementSlow(start, size, result);
}
return false;
}
inline void MallocHook::InvokeMremapHook(const void* result,
const void* old_addr,
size_t old_size,
size_t new_size,
int flags,
const void* new_addr) {
if (!absl::base_internal::mremap_hooks_.empty()) {
InvokeMremapHookSlow(result, old_addr, old_size, new_size, flags, new_addr);
}
}
inline void MallocHook::InvokePreSbrkHook(ptrdiff_t increment) {
if (!absl::base_internal::presbrk_hooks_.empty() && increment != 0) {
InvokePreSbrkHookSlow(increment);
}
}
inline void MallocHook::InvokeSbrkHook(const void* result,
ptrdiff_t increment) {
if (!absl::base_internal::sbrk_hooks_.empty() && increment != 0) {
InvokeSbrkHookSlow(result, increment);
}
}
} // namespace base_internal
} // namespace absl
#endif // ABSL_BASE_INTERNAL_MALLOC_HOOK_INVOKE_H_
// 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
//
// 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.
//
// We define mmap() and mmap64(), which somewhat reimplements libc's mmap
// syscall stubs. Unfortunately libc only exports the stubs via weak symbols
// (which we're overriding with our mmap64() and mmap() wrappers) so we can't
// just call through to them.
#ifndef __linux__
# error Should only be including malloc_hook_mmap_linux.h on linux systems.
#endif
#include <sys/mman.h>
#include <sys/types.h>
#ifdef __BIONIC__
#include <sys/syscall.h>
#else
#include <syscall.h>
#endif
#include <linux/unistd.h>
#include <unistd.h>
#include <cerrno>
#include <cstdarg>
#include <cstdint>
#include "absl/base/internal/direct_mmap.h"
// SYS_mremap is not defined in Android.
#ifdef __BIONIC__
#ifndef SYS_mremap
#define SYS_mremap __NR_mremap
#endif
#endif // __BIONIC__
// We put MallocHook::InvokeMmapHook calls right into mmap and mmap64, so that
// the stack frames in the caller's stack are at the same offsets for all the
// calls of memory allocating functions.
// Put all callers of MallocHook::Invoke* in this module into
// malloc_hook section,
// so that MallocHook::GetCallerStackTrace can function accurately:
// Make sure mmap doesn't get #define'd away by <sys/mman.h>
# undef mmap
extern "C" {
ABSL_ATTRIBUTE_SECTION(malloc_hook)
void* mmap64(void* start, size_t length, int prot, int flags, int fd,
off64_t offset) __THROW;
ABSL_ATTRIBUTE_SECTION(malloc_hook)
void* mmap(void* start, size_t length, int prot, int flags, int fd,
off_t offset) __THROW;
ABSL_ATTRIBUTE_SECTION(malloc_hook)
int munmap(void* start, size_t length) __THROW;
ABSL_ATTRIBUTE_SECTION(malloc_hook)
void* mremap(void* old_addr, size_t old_size, size_t new_size, int flags,
...) __THROW;
ABSL_ATTRIBUTE_SECTION(malloc_hook) void* sbrk(ptrdiff_t increment) __THROW;
}
extern "C" void* mmap64(void *start, size_t length, int prot, int flags,
int fd, off64_t offset) __THROW {
absl::base_internal::MallocHook::InvokePreMmapHook(start, length, prot, flags,
fd, offset);
void *result;
if (!absl::base_internal::MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = absl::base_internal::DirectMmap(start, length, prot, flags, fd,
offset);
}
absl::base_internal::MallocHook::InvokeMmapHook(result, start, length, prot,
flags, fd, offset);
return result;
}
# if !defined(__USE_FILE_OFFSET64) || !defined(__REDIRECT_NTH)
extern "C" void* mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset) __THROW {
absl::base_internal::MallocHook::InvokePreMmapHook(start, length, prot, flags,
fd, offset);
void *result;
if (!absl::base_internal::MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = absl::base_internal::DirectMmap(
start, length, prot, flags, fd,
static_cast<size_t>(offset)); // avoid sign extension
}
absl::base_internal::MallocHook::InvokeMmapHook(result, start, length, prot,
flags, fd, offset);
return result;
}
# endif // !defined(__USE_FILE_OFFSET64) || !defined(__REDIRECT_NTH)
extern "C" int munmap(void* start, size_t length) __THROW {
absl::base_internal::MallocHook::InvokeMunmapHook(start, length);
int result;
if (!absl::base_internal::MallocHook::InvokeMunmapReplacement(start, length,
&result)) {
result = absl::base_internal::DirectMunmap(start, length);
}
return result;
}
extern "C" void* mremap(void* old_addr, size_t old_size, size_t new_size,
int flags, ...) __THROW {
va_list ap;
va_start(ap, flags);
void *new_address = va_arg(ap, void *);
va_end(ap);
void* result = reinterpret_cast<void*>(
syscall(SYS_mremap, old_addr, old_size, new_size, flags, new_address));
absl::base_internal::MallocHook::InvokeMremapHook(
result, old_addr, old_size, new_size, flags, new_address);
return result;
}
// sbrk cannot be intercepted on Android as there is no mechanism to
// invoke the original sbrk (since there is no __sbrk as with glibc).
#if !defined(__BIONIC__)
// libc's version:
extern "C" void* __sbrk(ptrdiff_t increment);
extern "C" void* sbrk(ptrdiff_t increment) __THROW {
absl::base_internal::MallocHook::InvokePreSbrkHook(increment);
void *result = __sbrk(increment);
absl::base_internal::MallocHook::InvokeSbrkHook(result, increment);
return result;
}
#endif // !defined(__BIONIC__)
namespace absl {
namespace base_internal {
/*static*/void* MallocHook::UnhookedMMap(void *start, size_t length, int prot,
int flags, int fd, off_t offset) {
void* result;
if (!MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = absl::base_internal::DirectMmap(start, length, prot, flags, fd,
offset);
}
return result;
}
/*static*/int MallocHook::UnhookedMUnmap(void *start, size_t length) {
int result;
if (!MallocHook::InvokeMunmapReplacement(start, length, &result)) {
result = absl::base_internal::DirectMunmap(start, length);
}
return result;
}
} // namespace base_internal
} // namespace absl
...@@ -207,7 +207,7 @@ class FixedArray { ...@@ -207,7 +207,7 @@ class FixedArray {
// Overload of FixedArray::at() to return a const reference to the ith element // Overload of FixedArray::at() to return a const reference to the ith element
// of the fixed array. // of the fixed array.
const_reference at(size_type i) const { const_reference at(size_type i) const {
if (i >= size()) { if (ABSL_PREDICT_FALSE(i >= size())) {
base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
} }
return data()[i]; return data()[i];
......
...@@ -651,7 +651,7 @@ TEST(IntVec, AliasingCopyAssignment) { ...@@ -651,7 +651,7 @@ TEST(IntVec, AliasingCopyAssignment) {
IntVec original; IntVec original;
Fill(&original, len); Fill(&original, len);
IntVec dup = original; IntVec dup = original;
dup = dup; dup = *&dup;
EXPECT_EQ(dup, original); EXPECT_EQ(dup, original);
} }
} }
......
...@@ -76,6 +76,24 @@ cc_test( ...@@ -76,6 +76,24 @@ cc_test(
) )
cc_library( cc_library(
name = "examine_stack",
srcs = [
"internal/examine_stack.cc",
],
hdrs = [
"internal/examine_stack.h",
],
copts = ABSL_DEFAULT_COPTS,
visibility = ["//visibility:private"],
deps = [
":stacktrace",
":symbolize",
"//absl/base",
"//absl/base:core_headers",
],
)
cc_library(
name = "debugging_internal", name = "debugging_internal",
srcs = [ srcs = [
"internal/address_is_readable.cc", "internal/address_is_readable.cc",
...@@ -234,11 +252,11 @@ cc_library( ...@@ -234,11 +252,11 @@ cc_library(
srcs = ["internal/stack_consumption.cc"], srcs = ["internal/stack_consumption.cc"],
hdrs = ["internal/stack_consumption.h"], hdrs = ["internal/stack_consumption.h"],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
visibility = ["//visibility:private"],
deps = [ deps = [
"//absl/base", "//absl/base",
"//absl/base:core_headers", "//absl/base:core_headers",
], ],
visibility = ["//visibility:private"],
) )
cc_test( cc_test(
......
...@@ -25,6 +25,7 @@ list(APPEND DEBUGGING_INTERNAL_HEADERS ...@@ -25,6 +25,7 @@ list(APPEND DEBUGGING_INTERNAL_HEADERS
"internal/address_is_readable.h" "internal/address_is_readable.h"
"internal/demangle.h" "internal/demangle.h"
"internal/elf_mem_image.h" "internal/elf_mem_image.h"
"internal/examine_stack.h"
"internal/stacktrace_config.h" "internal/stacktrace_config.h"
"internal/symbolize.h" "internal/symbolize.h"
"internal/vdso_support.h" "internal/vdso_support.h"
...@@ -49,6 +50,12 @@ list(APPEND SYMBOLIZE_SRC ...@@ -49,6 +50,12 @@ list(APPEND SYMBOLIZE_SRC
${DEBUGGING_INTERNAL_HEADERS} ${DEBUGGING_INTERNAL_HEADERS}
) )
list(APPEND EXAMINE_STACK_SRC
"internal/examine_stack.cc"
${DEBUGGING_PUBLIC_HEADERS}
${DEBUGGING_INTERNAL_HEADERS}
)
absl_library( absl_library(
TARGET TARGET
absl_stacktrace absl_stacktrace
...@@ -67,6 +74,17 @@ absl_library( ...@@ -67,6 +74,17 @@ absl_library(
symbolize symbolize
) )
# Internal-only. Projects external to Abseil should not depend
# directly on this library.
absl_library(
TARGET
absl_examine_stack
SOURCES
${EXAMINE_STACK_SRC}
EXPORT_NAME
examine_stack
)
list(APPEND LEAK_CHECK_SRC list(APPEND LEAK_CHECK_SRC
"leak_check.cc" "leak_check.cc"
) )
......
//
// 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.
//
#include "absl/debugging/internal/examine_stack.h"
#ifndef _WIN32
#include <unistd.h>
#endif
#include <csignal>
#include <cstdio>
#include "absl/base/attributes.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
namespace absl {
namespace debugging_internal {
// Returns the program counter from signal context, nullptr if
// unknown. vuc is a ucontext_t*. We use void* to avoid the use of
// ucontext_t on non-POSIX systems.
void* GetProgramCounter(void* vuc) {
#ifdef __linux__
if (vuc != nullptr) {
ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc);
#if defined(__aarch64__)
return reinterpret_cast<void*>(context->uc_mcontext.pc);
#elif defined(__arm__)
return reinterpret_cast<void*>(context->uc_mcontext.arm_pc);
#elif defined(__i386__)
if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]);
#elif defined(__powerpc64__)
return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
#elif defined(__powerpc__)
return reinterpret_cast<void*>(context->uc_mcontext.regs->nip);
#elif defined(__x86_64__)
if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
#else
#error "Undefined Architecture."
#endif
}
#elif defined(__akaros__)
auto* ctx = reinterpret_cast<struct user_context*>(vuc);
return reinterpret_cast<void*>(get_user_ctx_pc(ctx));
#endif
static_cast<void>(vuc);
return nullptr;
}
// The %p field width for printf() functions is two characters per byte,
// and two extra for the leading "0x".
static constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
// Print a program counter, its stack frame size, and its symbol name.
// Note that there is a separate symbolize_pc argument. Return addresses may be
// at the end of the function, and this allows the caller to back up from pc if
// appropriate.
static void DumpPCAndFrameSizeAndSymbol(void (*writerfn)(const char*, void*),
void* writerfn_arg, void* pc,
void* symbolize_pc, int framesize,
const char* const prefix) {
char tmp[1024];
const char* symbol = "(unknown)";
if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) {
symbol = tmp;
}
char buf[1024];
if (framesize <= 0) {
snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix,
kPrintfPointerFieldWidth, pc, symbol);
} else {
snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix,
kPrintfPointerFieldWidth, pc, framesize, symbol);
}
writerfn(buf, writerfn_arg);
}
// Print a program counter and the corresponding stack frame size.
static void DumpPCAndFrameSize(void (*writerfn)(const char*, void*),
void* writerfn_arg, void* pc, int framesize,
const char* const prefix) {
char buf[100];
if (framesize <= 0) {
snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix,
kPrintfPointerFieldWidth, pc);
} else {
snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix,
kPrintfPointerFieldWidth, pc, framesize);
}
writerfn(buf, writerfn_arg);
}
void DumpPCAndFrameSizesAndStackTrace(
void* pc, void* const stack[], int frame_sizes[], int depth,
int min_dropped_frames, bool symbolize_stacktrace,
void (*writerfn)(const char*, void*), void* writerfn_arg) {
if (pc != nullptr) {
// We don't know the stack frame size for PC, use 0.
if (symbolize_stacktrace) {
DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, pc, pc, 0, "PC: ");
} else {
DumpPCAndFrameSize(writerfn, writerfn_arg, pc, 0, "PC: ");
}
}
for (int i = 0; i < depth; i++) {
if (symbolize_stacktrace) {
// Pass the previous address of pc as the symbol address because pc is a
// return address, and an overrun may occur when the function ends with a
// call to a function annotated noreturn (e.g. CHECK). Note that we don't
// do this for pc above, as the adjustment is only correct for return
// addresses.
DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, stack[i],
reinterpret_cast<char*>(stack[i]) - 1,
frame_sizes[i], " ");
} else {
DumpPCAndFrameSize(writerfn, writerfn_arg, stack[i], frame_sizes[i],
" ");
}
}
if (min_dropped_frames > 0) {
char buf[100];
snprintf(buf, sizeof(buf), " @ ... and at least %d more frames\n",
min_dropped_frames);
writerfn(buf, writerfn_arg);
}
}
} // namespace debugging_internal
} // namespace absl
//
// 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.
//
#ifndef ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
namespace absl {
namespace debugging_internal {
// Returns the program counter from signal context, or nullptr if
// unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of
// ucontext_t on non-POSIX systems.
void* GetProgramCounter(void* vuc);
// Uses `writerfn` to dump the program counter, stack trace, and stack
// frame sizes.
void DumpPCAndFrameSizesAndStackTrace(
void* pc, void* const stack[], int frame_sizes[], int depth,
int min_dropped_frames, bool symbolize_stacktrace,
void (*writerfn)(const char*, void*), void* writerfn_arg);
} // namespace debugging_internal
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
...@@ -168,13 +168,13 @@ class string_view { ...@@ -168,13 +168,13 @@ class string_view {
string_view( // NOLINT(runtime/explicit) string_view( // NOLINT(runtime/explicit)
const std::basic_string<char, std::char_traits<char>, Allocator>& const std::basic_string<char, std::char_traits<char>, Allocator>&
str) noexcept str) noexcept
: ptr_(str.data()), length_(str.size()) {} : ptr_(str.data()), length_(CheckLengthInternal(str.size())) {}
// Implicit constructor of a `string_view` from nul-terminated `str`. When // Implicit constructor of a `string_view` from nul-terminated `str`. When
// accepting possibly null strings, use `absl::NullSafeStringView(str)` // accepting possibly null strings, use `absl::NullSafeStringView(str)`
// instead (see below). // instead (see below).
constexpr string_view(const char* str) // NOLINT(runtime/explicit) constexpr string_view(const char* str) // NOLINT(runtime/explicit)
: ptr_(str), length_(StrLenInternal(str)) {} : ptr_(str), length_(CheckLengthInternal(StrLenInternal(str))) {}
// Implicit constructor of a `string_view` from a `const char*` and length. // Implicit constructor of a `string_view` from a `const char*` and length.
constexpr string_view(const char* data, size_type len) constexpr string_view(const char* data, size_type len)
...@@ -479,7 +479,7 @@ class string_view { ...@@ -479,7 +479,7 @@ class string_view {
private: private:
static constexpr size_type kMaxSize = static constexpr size_type kMaxSize =
std::numeric_limits<size_type>::max() / 2 + 1; std::numeric_limits<difference_type>::max();
// check whether __builtin_strlen is provided by the compiler. // check whether __builtin_strlen is provided by the compiler.
// GCC doesn't have __has_builtin() // GCC doesn't have __has_builtin()
......
...@@ -1068,6 +1068,17 @@ TEST(HugeStringView, TwoPointTwoGB) { ...@@ -1068,6 +1068,17 @@ TEST(HugeStringView, TwoPointTwoGB) {
TEST(NonNegativeLenTest, NonNegativeLen) { TEST(NonNegativeLenTest, NonNegativeLen) {
EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1), "len <= kMaxSize"); EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1), "len <= kMaxSize");
} }
TEST(LenExceedsMaxSizeTest, LenExceedsMaxSize) {
auto max_size = absl::string_view().max_size();
// This should construct ok (although the view itself is obviously invalid).
absl::string_view ok_view("", max_size);
// Adding one to the max should trigger an assertion.
EXPECT_DEATH_IF_SUPPORTED(absl::string_view("", max_size + 1),
"len <= kMaxSize");
}
#endif // !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW) #endif // !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW)
class StringViewStreamTest : public ::testing::Test { class StringViewStreamTest : public ::testing::Test {
......
...@@ -75,7 +75,6 @@ cc_library( ...@@ -75,7 +75,6 @@ cc_library(
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:dynamic_annotations", "//absl/base:dynamic_annotations",
"//absl/base:malloc_extension",
"//absl/base:malloc_internal", "//absl/base:malloc_internal",
"//absl/debugging:stacktrace", "//absl/debugging:stacktrace",
"//absl/time", "//absl/time",
...@@ -168,7 +167,6 @@ cc_library( ...@@ -168,7 +167,6 @@ cc_library(
deps = [ deps = [
":synchronization", ":synchronization",
"//absl/base", "//absl/base",
"//absl/base:malloc_extension",
"//absl/strings", "//absl/strings",
"//absl/time", "//absl/time",
"@com_google_googletest//:gtest", "@com_google_googletest//:gtest",
...@@ -184,7 +182,6 @@ cc_test( ...@@ -184,7 +182,6 @@ cc_test(
":per_thread_sem_test_common", ":per_thread_sem_test_common",
":synchronization", ":synchronization",
"//absl/base", "//absl/base",
"//absl/base:malloc_extension",
"//absl/strings", "//absl/strings",
"//absl/time", "//absl/time",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
......
...@@ -44,7 +44,7 @@ list(APPEND SYNCHRONIZATION_SRC ...@@ -44,7 +44,7 @@ list(APPEND SYNCHRONIZATION_SRC
"notification.cc" "notification.cc"
"mutex.cc" "mutex.cc"
) )
set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl_malloc_extension absl::time) set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl::time)
absl_library( absl_library(
TARGET TARGET
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include <atomic> #include <atomic>
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/internal/malloc_extension.h"
#include "absl/base/internal/thread_identity.h" #include "absl/base/internal/thread_identity.h"
#include "absl/synchronization/internal/waiter.h" #include "absl/synchronization/internal/waiter.h"
...@@ -90,12 +89,6 @@ ABSL_ATTRIBUTE_WEAK bool AbslInternalPerThreadSemWait( ...@@ -90,12 +89,6 @@ ABSL_ATTRIBUTE_WEAK bool AbslInternalPerThreadSemWait(
if (identity->blocked_count_ptr != nullptr) { if (identity->blocked_count_ptr != nullptr) {
identity->blocked_count_ptr->fetch_sub(1, std::memory_order_relaxed); identity->blocked_count_ptr->fetch_sub(1, std::memory_order_relaxed);
} }
if (identity->is_idle.load(std::memory_order_relaxed)) {
// We became idle during the wait; become non-idle again so that
// performance of deallocations done from now on does not suffer.
absl::base_internal::MallocExtension::instance()->MarkThreadBusy();
}
identity->is_idle.store(false, std::memory_order_relaxed); identity->is_idle.store(false, std::memory_order_relaxed);
identity->wait_start.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed);
return !timeout; return !timeout;
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/cycleclock.h" #include "absl/base/internal/cycleclock.h"
#include "absl/base/internal/malloc_extension.h"
#include "absl/base/internal/thread_identity.h" #include "absl/base/internal/thread_identity.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/time/clock.h" #include "absl/time/clock.h"
...@@ -167,79 +166,6 @@ TEST_F(PerThreadSemTest, Timeouts) { ...@@ -167,79 +166,6 @@ TEST_F(PerThreadSemTest, Timeouts) {
EXPECT_TRUE(Wait(negative_timeout)); EXPECT_TRUE(Wait(negative_timeout));
} }
// Test that idle threads properly register themselves as such with malloc.
TEST_F(PerThreadSemTest, Idle) {
// We can't use gmock because it might use synch calls. So we do it
// by hand, messily. I don't bother hitting every one of the
// MallocExtension calls because most of them won't get made
// anyway--if they do we can add them.
class MockMallocExtension : public base_internal::MallocExtension {
public:
MockMallocExtension(base_internal::MallocExtension *real,
base_internal::ThreadIdentity *id,
std::atomic<int> *idles, std::atomic<int> *busies)
: real_(real), id_(id), idles_(idles), busies_(busies) {}
void MarkThreadIdle() override {
if (base_internal::CurrentThreadIdentityIfPresent() != id_) {
return;
}
idles_->fetch_add(1, std::memory_order_relaxed);
}
void MarkThreadBusy() override {
if (base_internal::CurrentThreadIdentityIfPresent() != id_) {
return;
}
busies_->fetch_add(1, std::memory_order_relaxed);
}
size_t GetAllocatedSize(const void* p) override {
return real_->GetAllocatedSize(p);
}
private:
MallocExtension *real_;
base_internal::ThreadIdentity *id_;
std::atomic<int>* idles_;
std::atomic<int>* busies_;
};
base_internal::ThreadIdentity *id = GetOrCreateCurrentThreadIdentity();
std::atomic<int> idles(0);
std::atomic<int> busies(0);
base_internal::MallocExtension *old =
base_internal::MallocExtension::instance();
MockMallocExtension mock(old, id, &idles, &busies);
base_internal::MallocExtension::Register(&mock);
std::atomic<int> sync(0);
std::thread t([id, &idles, &sync]() {
// Wait for the main thread to begin the wait process
while (0 == sync.load(std::memory_order_relaxed)) {
SleepFor(absl::Milliseconds(1));
}
// Wait for main thread to become idle, then wake it
// pretend time is passing--enough of these should cause an idling.
for (int i = 0; i < 100; ++i) {
Tick(id);
}
while (0 == idles.load(std::memory_order_relaxed)) {
// Keep ticking, just in case.
Tick(id);
SleepFor(absl::Milliseconds(1));
}
Post(id);
});
idles.store(0, std::memory_order_relaxed); // In case we slept earlier.
sync.store(1, std::memory_order_relaxed);
Wait(KernelTimeout::Never());
// t will wake us once we become idle.
EXPECT_LT(0, busies.load(std::memory_order_relaxed));
t.join();
base_internal::MallocExtension::Register(old);
}
} // namespace } // namespace
} // namespace synchronization_internal } // namespace synchronization_internal
......
...@@ -40,8 +40,6 @@ ...@@ -40,8 +40,6 @@
#include <atomic> #include <atomic>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include "absl/base/internal/malloc_extension.h"
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/thread_identity.h" #include "absl/base/internal/thread_identity.h"
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
...@@ -59,7 +57,6 @@ static void MaybeBecomeIdle() { ...@@ -59,7 +57,6 @@ static void MaybeBecomeIdle() {
const int wait_start = identity->wait_start.load(std::memory_order_relaxed); const int wait_start = identity->wait_start.load(std::memory_order_relaxed);
if (!is_idle && ticker - wait_start > Waiter::kIdlePeriods) { if (!is_idle && ticker - wait_start > Waiter::kIdlePeriods) {
identity->is_idle.store(true, std::memory_order_relaxed); identity->is_idle.store(true, std::memory_order_relaxed);
base_internal::MallocExtension::instance()->MarkThreadIdle();
} }
} }
......
...@@ -174,8 +174,10 @@ inline Duration MakeDurationFromU128(uint128 u128, bool is_neg) { ...@@ -174,8 +174,10 @@ inline Duration MakeDurationFromU128(uint128 u128, bool is_neg) {
// name intN_t designates a signed integer type with width N, no padding // name intN_t designates a signed integer type with width N, no padding
// bits, and a two's complement representation." So, we can convert to // bits, and a two's complement representation." So, we can convert to
// and from the corresponding uint64_t value using a bit cast. // and from the corresponding uint64_t value using a bit cast.
inline uint64_t EncodeTwosComp(int64_t v) { return bit_cast<uint64_t>(v); } inline uint64_t EncodeTwosComp(int64_t v) {
inline int64_t DecodeTwosComp(uint64_t v) { return bit_cast<int64_t>(v); } return absl::bit_cast<uint64_t>(v);
}
inline int64_t DecodeTwosComp(uint64_t v) { return absl::bit_cast<int64_t>(v); }
// Note: The overflow detection in this function is done using greater/less *or // Note: The overflow detection in this function is done using greater/less *or
// equal* because kint64max/min is too large to be represented exactly in a // equal* because kint64max/min is too large to be represented exactly in a
......
...@@ -21,15 +21,21 @@ ...@@ -21,15 +21,21 @@
#include "absl/base/internal/exception_safety_testing.h" #include "absl/base/internal/exception_safety_testing.h"
using Thrower = absl::ThrowingValue<>; using Thrower = absl::ThrowingValue<>;
using NoThrowMoveThrower =
absl::ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>;
using ThrowerList = std::initializer_list<Thrower>; using ThrowerList = std::initializer_list<Thrower>;
using ThrowerVec = std::vector<Thrower>; using ThrowerVec = std::vector<Thrower>;
using ThrowingAlloc = absl::ThrowingAllocator<Thrower>; using ThrowingAlloc = absl::ThrowingAllocator<Thrower>;
using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>; using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>;
namespace absl { namespace {
class AnyExceptionSafety : public ::testing::Test {
private:
absl::ConstructorTracker inspector_;
};
testing::AssertionResult AbslCheckInvariants(absl::any* a, testing::AssertionResult AnyInvariants(absl::any* a) {
InternalAbslNamespaceFinder) {
using testing::AssertionFailure; using testing::AssertionFailure;
using testing::AssertionSuccess; using testing::AssertionSuccess;
...@@ -69,17 +75,10 @@ testing::AssertionResult AbslCheckInvariants(absl::any* a, ...@@ -69,17 +75,10 @@ testing::AssertionResult AbslCheckInvariants(absl::any* a,
return AssertionSuccess(); return AssertionSuccess();
} }
} // namespace absl
namespace {
class AnyExceptionSafety : public ::testing::Test {
private:
absl::ConstructorTracker inspector_;
};
testing::AssertionResult AnyIsEmpty(absl::any* a) { testing::AssertionResult AnyIsEmpty(absl::any* a) {
if (!a->has_value()) return testing::AssertionSuccess(); if (!a->has_value()) {
return testing::AssertionSuccess();
}
return testing::AssertionFailure() return testing::AssertionFailure()
<< "a should be empty, but instead has value " << "a should be empty, but instead has value "
<< absl::any_cast<Thrower>(*a).Get(); << absl::any_cast<Thrower>(*a).Get();
...@@ -100,101 +99,70 @@ TEST_F(AnyExceptionSafety, Ctors) { ...@@ -100,101 +99,70 @@ TEST_F(AnyExceptionSafety, Ctors) {
absl::in_place_type_t<ThrowingThrowerVec>(), {val}, ThrowingAlloc()); absl::in_place_type_t<ThrowingThrowerVec>(), {val}, ThrowingAlloc());
} }
struct OneFactory {
std::unique_ptr<absl::any> operator()() const {
return absl::make_unique<absl::any>(absl::in_place_type_t<Thrower>(), 1,
absl::no_throw_ctor);
}
};
struct EmptyFactory {
std::unique_ptr<absl::any> operator()() const {
return absl::make_unique<absl::any>();
}
};
TEST_F(AnyExceptionSafety, Assignment) { TEST_F(AnyExceptionSafety, Assignment) {
auto thrower_comp = [](const absl::any& l, const absl::any& r) { auto original =
return absl::any_cast<Thrower>(l) == absl::any_cast<Thrower>(r); absl::any(absl::in_place_type_t<Thrower>(), 1, absl::no_throw_ctor);
auto any_is_strong = [original](absl::any* ap) {
return testing::AssertionResult(ap->has_value() &&
absl::any_cast<Thrower>(original) ==
absl::any_cast<Thrower>(*ap));
}; };
auto any_strong_tester = absl::MakeExceptionSafetyTester()
.WithInitialValue(original)
.WithInvariants(AnyInvariants, any_is_strong);
OneFactory one_factory;
absl::ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>
moveable_val(2);
Thrower val(2); Thrower val(2);
absl::any any_val(val); absl::any any_val(val);
NoThrowMoveThrower mv_val(2);
EXPECT_TRUE(absl::TestExceptionSafety( auto assign_any = [&any_val](absl::any* ap) { *ap = any_val; };
one_factory, [&any_val](absl::any* ap) { *ap = any_val; }, auto assign_val = [&val](absl::any* ap) { *ap = val; };
absl::StrongGuarantee(one_factory, thrower_comp))); auto move = [&val](absl::any* ap) { *ap = std::move(val); };
auto move_movable = [&mv_val](absl::any* ap) { *ap = std::move(mv_val); };
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory, [&val](absl::any* ap) { *ap = val; },
absl::StrongGuarantee(one_factory, thrower_comp)));
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory, [&val](absl::any* ap) { *ap = std::move(val); },
absl::StrongGuarantee(one_factory, thrower_comp)));
EXPECT_TRUE(absl::TestExceptionSafety( EXPECT_TRUE(any_strong_tester.Test(assign_any));
one_factory, EXPECT_TRUE(any_strong_tester.Test(assign_val));
[&moveable_val](absl::any* ap) { *ap = std::move(moveable_val); }, EXPECT_TRUE(any_strong_tester.Test(move));
absl::StrongGuarantee(one_factory, thrower_comp))); EXPECT_TRUE(any_strong_tester.Test(move_movable));
EmptyFactory empty_factory; auto empty_any_is_strong = [](absl::any* ap) {
auto empty_comp = [](const absl::any& l, const absl::any& r) { return testing::AssertionResult{!ap->has_value()};
return !(l.has_value() || r.has_value());
}; };
auto strong_empty_any_tester =
EXPECT_TRUE(absl::TestExceptionSafety( absl::MakeExceptionSafetyTester()
empty_factory, [&any_val](absl::any* ap) { *ap = any_val; }, .WithInitialValue(absl::any{})
absl::StrongGuarantee(empty_factory, empty_comp))); .WithInvariants(AnyInvariants, empty_any_is_strong);
EXPECT_TRUE(absl::TestExceptionSafety( EXPECT_TRUE(strong_empty_any_tester.Test(assign_any));
empty_factory, [&val](absl::any* ap) { *ap = val; }, EXPECT_TRUE(strong_empty_any_tester.Test(assign_val));
absl::StrongGuarantee(empty_factory, empty_comp))); EXPECT_TRUE(strong_empty_any_tester.Test(move));
EXPECT_TRUE(absl::TestExceptionSafety(
empty_factory, [&val](absl::any* ap) { *ap = std::move(val); },
absl::StrongGuarantee(empty_factory, empty_comp)));
} }
// libstdc++ std::any fails this test // libstdc++ std::any fails this test
#if !defined(ABSL_HAVE_STD_ANY) #if !defined(ABSL_HAVE_STD_ANY)
TEST_F(AnyExceptionSafety, Emplace) { TEST_F(AnyExceptionSafety, Emplace) {
OneFactory one_factory; auto initial_val =
absl::any{absl::in_place_type_t<Thrower>(), 1, absl::no_throw_ctor};
EXPECT_TRUE(absl::TestExceptionSafety( auto one_tester = absl::MakeExceptionSafetyTester()
one_factory, [](absl::any* ap) { ap->emplace<Thrower>(2); }, AnyIsEmpty)); .WithInitialValue(initial_val)
.WithInvariants(AnyInvariants, AnyIsEmpty);
EXPECT_TRUE(absl::TestExceptionSafety(
one_factory, auto emp_thrower = [](absl::any* ap) { ap->emplace<Thrower>(2); };
[](absl::any* ap) { auto emp_throwervec = [](absl::any* ap) {
ap->emplace<absl::ThrowingValue<absl::NoThrow::kMoveCtor | std::initializer_list<Thrower> il{Thrower(2, absl::no_throw_ctor)};
absl::NoThrow::kMoveAssign>>(2); ap->emplace<ThrowerVec>(il);
}, };
AnyIsEmpty)); auto emp_movethrower = [](absl::any* ap) {
ap->emplace<NoThrowMoveThrower>(2);
EXPECT_TRUE(absl::TestExceptionSafety(one_factory, };
[](absl::any* ap) {
std::initializer_list<Thrower> il{ EXPECT_TRUE(one_tester.Test(emp_thrower));
Thrower(2, absl::no_throw_ctor)}; EXPECT_TRUE(one_tester.Test(emp_throwervec));
ap->emplace<ThrowerVec>(il); EXPECT_TRUE(one_tester.Test(emp_movethrower));
},
AnyIsEmpty)); auto empty_tester = one_tester.WithInitialValue(absl::any{});
EmptyFactory empty_factory; EXPECT_TRUE(empty_tester.Test(emp_thrower));
EXPECT_TRUE(absl::TestExceptionSafety( EXPECT_TRUE(empty_tester.Test(emp_throwervec));
empty_factory, [](absl::any* ap) { ap->emplace<Thrower>(2); },
AnyIsEmpty));
EXPECT_TRUE(absl::TestExceptionSafety(empty_factory,
[](absl::any* ap) {
std::initializer_list<Thrower> il{
Thrower(2, absl::no_throw_ctor)};
ap->emplace<ThrowerVec>(il);
},
AnyIsEmpty));
} }
#endif // ABSL_HAVE_STD_ANY #endif // ABSL_HAVE_STD_ANY
......
...@@ -553,7 +553,7 @@ TEST(AnyTest, Move) { ...@@ -553,7 +553,7 @@ TEST(AnyTest, Move) {
absl::any tmp4(4); absl::any tmp4(4);
absl::any o4(std::move(tmp4)); // move construct absl::any o4(std::move(tmp4)); // move construct
EXPECT_EQ(4, absl::any_cast<int>(o4)); EXPECT_EQ(4, absl::any_cast<int>(o4));
o4 = o4; // self assign o4 = *&o4; // self assign
EXPECT_EQ(4, absl::any_cast<int>(o4)); EXPECT_EQ(4, absl::any_cast<int>(o4));
EXPECT_TRUE(o4.has_value()); EXPECT_TRUE(o4.has_value());
......
...@@ -216,7 +216,7 @@ using EnableIfConvertibleToSpanConst = ...@@ -216,7 +216,7 @@ using EnableIfConvertibleToSpanConst =
// // Construct a Span implicitly from a container // // Construct a Span implicitly from a container
// void MyRoutine(absl::Span<const int> a) { // void MyRoutine(absl::Span<const int> a) {
// ... // ...
// }; // }
// std::vector v = {1,2,3,4,5}; // std::vector v = {1,2,3,4,5};
// MyRoutine(v) // convert to Span<const T> // MyRoutine(v) // convert to Span<const T>
// //
...@@ -235,7 +235,7 @@ using EnableIfConvertibleToSpanConst = ...@@ -235,7 +235,7 @@ using EnableIfConvertibleToSpanConst =
// //
// void MyRoutine(absl::Span<const int> a) { // void MyRoutine(absl::Span<const int> a) {
// ... // ...
// }; // }
// //
// std::vector v = {1,2,3,4,5}; // std::vector v = {1,2,3,4,5};
// MyRoutine(v); // MyRoutine(v);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment