summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2018-04-18 05:56:39 -0700
committerGravatar Jon Cohen <cohenjon@google.com>2018-04-19 10:19:32 -0400
commit5b535401665cc6aa96d54a5c9b0901153d97210f (patch)
tree8d45863656540c8ec81eb7d2742c9191c33ca935
parenta7e522daf1ec9cda69b356472f662142dd0c1215 (diff)
- 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
-rw-r--r--absl/base/BUILD.bazel49
-rw-r--r--absl/base/CMakeLists.txt42
-rw-r--r--absl/base/casts.h3
-rw-r--r--absl/base/exception_safety_testing_test.cc324
-rw-r--r--absl/base/internal/direct_mmap.h8
-rw-r--r--absl/base/internal/exception_safety_testing.cc3
-rw-r--r--absl/base/internal/exception_safety_testing.h392
-rw-r--r--absl/base/internal/low_level_alloc.cc19
-rw-r--r--absl/base/internal/low_level_alloc_test.cc52
-rw-r--r--absl/base/internal/malloc_extension.cc162
-rw-r--r--absl/base/internal/malloc_extension.h426
-rw-r--r--absl/base/internal/malloc_extension_c.h75
-rw-r--r--absl/base/internal/malloc_extension_test.cc79
-rw-r--r--absl/base/internal/malloc_hook.cc574
-rw-r--r--absl/base/internal/malloc_hook.h284
-rw-r--r--absl/base/internal/malloc_hook_c.h69
-rw-r--r--absl/base/internal/malloc_hook_invoke.h198
-rw-r--r--absl/base/internal/malloc_hook_mmap_linux.inc168
-rw-r--r--absl/container/fixed_array.h2
-rw-r--r--absl/container/inlined_vector_test.cc2
-rw-r--r--absl/debugging/BUILD.bazel20
-rw-r--r--absl/debugging/CMakeLists.txt18
-rw-r--r--absl/debugging/internal/examine_stack.cc147
-rw-r--r--absl/debugging/internal/examine_stack.h38
-rw-r--r--absl/strings/string_view.h6
-rw-r--r--absl/strings/string_view_test.cc11
-rw-r--r--absl/synchronization/BUILD.bazel3
-rw-r--r--absl/synchronization/CMakeLists.txt2
-rw-r--r--absl/synchronization/internal/per_thread_sem.cc7
-rw-r--r--absl/synchronization/internal/per_thread_sem_test.cc74
-rw-r--r--absl/synchronization/internal/waiter.cc3
-rw-r--r--absl/time/duration.cc6
-rw-r--r--absl/types/any_exception_safety_test.cc158
-rw-r--r--absl/types/any_test.cc2
-rw-r--r--absl/types/span.h4
35 files changed, 830 insertions, 2600 deletions
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index 707f4882..f9aac5a5 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -78,41 +78,15 @@ 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",
srcs = [
"internal/low_level_alloc.cc",
- "internal/malloc_hook.cc",
- "internal/malloc_hook_mmap_linux.inc",
],
hdrs = [
"internal/direct_mmap.h",
"internal/low_level_alloc.h",
- "internal/malloc_hook.h",
- "internal/malloc_hook_c.h",
],
copts = ABSL_DEFAULT_COPTS,
- textual_hdrs = [
- "internal/malloc_hook_invoke.h",
- ],
visibility = [
"//absl:__subpackages__",
],
@@ -419,26 +393,3 @@ cc_test(
"@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",
- ],
-)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index ced81b6b..329a3d05 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -41,10 +41,6 @@ list(APPEND BASE_INTERNAL_HEADERS
"internal/inline_variable.h"
"internal/low_level_alloc.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/pretty_function.h"
"internal/raw_logging.h"
@@ -69,7 +65,6 @@ list(APPEND BASE_SRC
"internal/thread_identity.cc"
"internal/unscaledcycleclock.cc"
"internal/low_level_alloc.cc"
- "internal/malloc_hook.cc"
${BASE_PUBLIC_HEADERS}
${BASE_INTERNAL_HEADERS}
)
@@ -86,21 +81,6 @@ absl_library(
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
set(THROW_DELEGATE_SRC "internal/throw_delegate.cc")
@@ -165,8 +145,6 @@ absl_library(
# malloc_internal library
list(APPEND MALLOC_INTERNAL_SRC
"internal/low_level_alloc.cc"
- "internal/malloc_hook.cc"
- "internal/malloc_hook_mmap_linux.inc"
)
absl_library(
@@ -378,23 +356,3 @@ absl_test(
PRIVATE_COMPILE_FLAGS
${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}
-)
-
-
-
-
diff --git a/absl/base/casts.h b/absl/base/casts.h
index 266c4a0a..3096d4c1 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -20,7 +20,6 @@
// 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,
// use these with caution and only if alternatives do not exist.
-//
#ifndef ABSL_BASE_CASTS_H_
#define ABSL_BASE_CASTS_H_
@@ -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
// 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
// issue by implementating its casts using `memcpy()`, which avoids introducing
// this undefined behavior.
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index 9c52f249..041d780f 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -402,29 +402,158 @@ TEST_F(ThrowingAllocatorTest, InList) {
for (int i = 0; i < 20; ++i) l.pop_front();
}
-struct CallOperator {
- template <typename T>
- void operator()(T* t) const {
- (*t)();
+template <typename TesterInstance, typename = void>
+struct NullaryTestValidator : public std::false_type {};
+
+template <typename TesterInstance>
+struct NullaryTestValidator<
+ TesterInstance,
+ absl::void_t<decltype(std::declval<TesterInstance>().Test())>>
+ : public std::true_type {};
+
+template <typename TesterInstance>
+bool HasNullaryTest(const TesterInstance&) {
+ return NullaryTestValidator<TesterInstance>::value;
+}
+
+void DummyOp(void*) {}
+
+template <typename TesterInstance, typename = void>
+struct UnaryTestValidator : public std::false_type {};
+
+template <typename TesterInstance>
+struct UnaryTestValidator<
+ TesterInstance,
+ absl::void_t<decltype(std::declval<TesterInstance>().Test(DummyOp))>>
+ : public std::true_type {};
+
+template <typename TesterInstance>
+bool HasUnaryTest(const TesterInstance&) {
+ return UnaryTestValidator<TesterInstance>::value;
+}
+
+TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
+ using T = exceptions_internal::UninitializedT;
+ auto op = [](T* t) {};
+ auto inv = [](T*) { return testing::AssertionSuccess(); };
+ auto fac = []() { return absl::make_unique<T>(); };
+
+ // Test that providing operation and inveriants still does not allow for the
+ // the invocation of .Test() and .Test(op) because it lacks a factory
+ auto without_fac =
+ absl::MakeExceptionSafetyTester().WithOperation(op).WithInvariants(
+ inv, absl::strong_guarantee);
+ EXPECT_FALSE(HasNullaryTest(without_fac));
+ EXPECT_FALSE(HasUnaryTest(without_fac));
+
+ // Test that providing invariants and factory allows the invocation of
+ // .Test(op) but does not allow for .Test() because it lacks an operation
+ auto without_op = absl::MakeExceptionSafetyTester()
+ .WithInvariants(inv, absl::strong_guarantee)
+ .WithFactory(fac);
+ EXPECT_FALSE(HasNullaryTest(without_op));
+ EXPECT_TRUE(HasUnaryTest(without_op));
+
+ // Test that providing operation and factory still does not allow for the
+ // the invocation of .Test() and .Test(op) because it lacks invariants
+ auto without_inv =
+ absl::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac);
+ EXPECT_FALSE(HasNullaryTest(without_inv));
+ EXPECT_FALSE(HasUnaryTest(without_inv));
+}
+
+struct ExampleStruct {};
+
+std::unique_ptr<ExampleStruct> ExampleFunctionFactory() {
+ return absl::make_unique<ExampleStruct>();
+}
+
+void ExampleFunctionOperation(ExampleStruct*) {}
+
+testing::AssertionResult ExampleFunctionInvariant(ExampleStruct*) {
+ return testing::AssertionSuccess();
+}
+
+struct {
+ std::unique_ptr<ExampleStruct> operator()() const {
+ return ExampleFunctionFactory();
+ }
+} example_struct_factory;
+
+struct {
+ void operator()(ExampleStruct*) const {}
+} example_struct_operation;
+
+struct {
+ testing::AssertionResult operator()(ExampleStruct* example_struct) const {
+ return ExampleFunctionInvariant(example_struct);
}
+} example_struct_invariant;
+
+auto example_lambda_factory = []() { return ExampleFunctionFactory(); };
+
+auto example_lambda_operation = [](ExampleStruct*) {};
+
+auto example_lambda_invariant = [](ExampleStruct* example_struct) {
+ return ExampleFunctionInvariant(example_struct);
};
+// Testing that function references, pointers, structs with operator() and
+// lambdas can all be used with ExceptionSafetyTester
+TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) {
+ // function reference
+ EXPECT_TRUE(absl::MakeExceptionSafetyTester()
+ .WithFactory(ExampleFunctionFactory)
+ .WithOperation(ExampleFunctionOperation)
+ .WithInvariants(ExampleFunctionInvariant)
+ .Test());
+
+ // function pointer
+ EXPECT_TRUE(absl::MakeExceptionSafetyTester()
+ .WithFactory(&ExampleFunctionFactory)
+ .WithOperation(&ExampleFunctionOperation)
+ .WithInvariants(&ExampleFunctionInvariant)
+ .Test());
+
+ // struct
+ EXPECT_TRUE(absl::MakeExceptionSafetyTester()
+ .WithFactory(example_struct_factory)
+ .WithOperation(example_struct_operation)
+ .WithInvariants(example_struct_invariant)
+ .Test());
+
+ // lambda
+ EXPECT_TRUE(absl::MakeExceptionSafetyTester()
+ .WithFactory(example_lambda_factory)
+ .WithOperation(example_lambda_operation)
+ .WithInvariants(example_lambda_invariant)
+ .Test());
+}
+
struct NonNegative {
- friend testing::AssertionResult AbslCheckInvariants(
- NonNegative* g, absl::InternalAbslNamespaceFinder) {
- if (g->i >= 0) return testing::AssertionSuccess();
- return testing::AssertionFailure()
- << "i should be non-negative but is " << g->i;
- }
bool operator==(const NonNegative& other) const { return i == other.i; }
-
int i;
};
-template <typename T>
-struct DefaultFactory {
- std::unique_ptr<T> operator()() const { return absl::make_unique<T>(); }
-};
+testing::AssertionResult CheckNonNegativeInvariants(NonNegative* g) {
+ if (g->i >= 0) {
+ return testing::AssertionSuccess();
+ }
+ return testing::AssertionFailure()
+ << "i should be non-negative but is " << g->i;
+}
+
+struct {
+ template <typename T>
+ void operator()(T* t) const {
+ (*t)();
+ }
+} invoker;
+
+auto tester =
+ absl::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants(
+ CheckNonNegativeInvariants);
+auto strong_tester = tester.WithInvariants(absl::strong_guarantee);
struct FailsBasicGuarantee : public NonNegative {
void operator()() {
@@ -435,8 +564,7 @@ struct FailsBasicGuarantee : public NonNegative {
};
TEST(ExceptionCheckTest, BasicGuaranteeFailure) {
- EXPECT_FALSE(TestExceptionSafety(DefaultFactory<FailsBasicGuarantee>(),
- CallOperator{}));
+ EXPECT_FALSE(tester.WithInitialValue(FailsBasicGuarantee{}).Test());
}
struct FollowsBasicGuarantee : public NonNegative {
@@ -447,22 +575,12 @@ struct FollowsBasicGuarantee : public NonNegative {
};
TEST(ExceptionCheckTest, BasicGuarantee) {
- EXPECT_TRUE(TestExceptionSafety(DefaultFactory<FollowsBasicGuarantee>(),
- CallOperator{}));
+ EXPECT_TRUE(tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
}
TEST(ExceptionCheckTest, StrongGuaranteeFailure) {
- {
- DefaultFactory<FailsBasicGuarantee> factory;
- EXPECT_FALSE(
- TestExceptionSafety(factory, CallOperator{}, StrongGuarantee(factory)));
- }
-
- {
- DefaultFactory<FollowsBasicGuarantee> factory;
- EXPECT_FALSE(
- TestExceptionSafety(factory, CallOperator{}, StrongGuarantee(factory)));
- }
+ EXPECT_FALSE(strong_tester.WithInitialValue(FailsBasicGuarantee{}).Test());
+ EXPECT_FALSE(strong_tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
}
struct BasicGuaranteeWithExtraInvariants : public NonNegative {
@@ -479,20 +597,21 @@ struct BasicGuaranteeWithExtraInvariants : public NonNegative {
constexpr int BasicGuaranteeWithExtraInvariants::kExceptionSentinel;
TEST(ExceptionCheckTest, BasicGuaranteeWithInvariants) {
- DefaultFactory<BasicGuaranteeWithExtraInvariants> factory;
-
- EXPECT_TRUE(TestExceptionSafety(factory, CallOperator{}));
-
- EXPECT_TRUE(TestExceptionSafety(
- factory, CallOperator{}, [](BasicGuaranteeWithExtraInvariants* w) {
- if (w->i == BasicGuaranteeWithExtraInvariants::kExceptionSentinel) {
- return testing::AssertionSuccess();
- }
- return testing::AssertionFailure()
- << "i should be "
- << BasicGuaranteeWithExtraInvariants::kExceptionSentinel
- << ", but is " << w->i;
- }));
+ auto tester_with_val =
+ tester.WithInitialValue(BasicGuaranteeWithExtraInvariants{});
+ EXPECT_TRUE(tester_with_val.Test());
+ EXPECT_TRUE(
+ tester_with_val
+ .WithInvariants([](BasicGuaranteeWithExtraInvariants* w) {
+ if (w->i == BasicGuaranteeWithExtraInvariants::kExceptionSentinel) {
+ return testing::AssertionSuccess();
+ }
+ return testing::AssertionFailure()
+ << "i should be "
+ << BasicGuaranteeWithExtraInvariants::kExceptionSentinel
+ << ", but is " << w->i;
+ })
+ .Test());
}
struct FollowsStrongGuarantee : public NonNegative {
@@ -500,10 +619,8 @@ struct FollowsStrongGuarantee : public NonNegative {
};
TEST(ExceptionCheckTest, StrongGuarantee) {
- DefaultFactory<FollowsStrongGuarantee> factory;
- EXPECT_TRUE(TestExceptionSafety(factory, CallOperator{}));
- EXPECT_TRUE(
- TestExceptionSafety(factory, CallOperator{}, StrongGuarantee(factory)));
+ EXPECT_TRUE(tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
+ EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
}
struct HasReset : public NonNegative {
@@ -514,38 +631,36 @@ struct HasReset : public NonNegative {
}
void reset() { i = 0; }
-
- friend bool AbslCheckInvariants(HasReset* h,
- absl::InternalAbslNamespaceFinder) {
- h->reset();
- return h->i == 0;
- }
};
+testing::AssertionResult CheckHasResetInvariants(HasReset* h) {
+ h->reset();
+ return testing::AssertionResult(h->i == 0);
+}
+
TEST(ExceptionCheckTest, ModifyingChecker) {
- {
- DefaultFactory<FollowsBasicGuarantee> factory;
- EXPECT_FALSE(TestExceptionSafety(
- factory, CallOperator{},
- [](FollowsBasicGuarantee* g) {
- g->i = 1000;
- return true;
- },
- [](FollowsBasicGuarantee* g) { return g->i == 1000; }));
- }
- {
- DefaultFactory<FollowsStrongGuarantee> factory;
- EXPECT_TRUE(TestExceptionSafety(factory, CallOperator{},
- [](FollowsStrongGuarantee* g) {
- ++g->i;
- return true;
- },
- StrongGuarantee(factory)));
- }
- {
- DefaultFactory<HasReset> factory;
- EXPECT_TRUE(TestExceptionSafety(factory, CallOperator{}));
- }
+ auto set_to_1000 = [](FollowsBasicGuarantee* g) {
+ g->i = 1000;
+ return testing::AssertionSuccess();
+ };
+ auto is_1000 = [](FollowsBasicGuarantee* g) {
+ return testing::AssertionResult(g->i == 1000);
+ };
+ auto increment = [](FollowsStrongGuarantee* g) {
+ ++g->i;
+ return testing::AssertionSuccess();
+ };
+
+ EXPECT_FALSE(tester.WithInitialValue(FollowsBasicGuarantee{})
+ .WithInvariants(set_to_1000, is_1000)
+ .Test());
+ EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
+ .WithInvariants(increment)
+ .Test());
+ EXPECT_TRUE(absl::MakeExceptionSafetyTester()
+ .WithInitialValue(HasReset{})
+ .WithInvariants(CheckHasResetInvariants)
+ .Test(invoker));
}
struct NonCopyable : public NonNegative {
@@ -556,10 +671,9 @@ struct NonCopyable : public NonNegative {
};
TEST(ExceptionCheckTest, NonCopyable) {
- DefaultFactory<NonCopyable> factory;
- EXPECT_TRUE(TestExceptionSafety(factory, CallOperator{}));
- EXPECT_TRUE(
- TestExceptionSafety(factory, CallOperator{}, StrongGuarantee(factory)));
+ auto factory = []() { return absl::make_unique<NonCopyable>(); };
+ EXPECT_TRUE(tester.WithFactory(factory).Test());
+ EXPECT_TRUE(strong_tester.WithFactory(factory).Test());
}
struct NonEqualityComparable : public NonNegative {
@@ -574,15 +688,15 @@ struct NonEqualityComparable : public NonNegative {
};
TEST(ExceptionCheckTest, NonEqualityComparable) {
- DefaultFactory<NonEqualityComparable> factory;
- auto comp = [](const NonEqualityComparable& a,
- const NonEqualityComparable& b) { return a.i == b.i; };
- EXPECT_TRUE(TestExceptionSafety(factory, CallOperator{}));
- EXPECT_TRUE(TestExceptionSafety(factory, CallOperator{},
- absl::StrongGuarantee(factory, comp)));
- EXPECT_FALSE(TestExceptionSafety(
- factory, [&](NonEqualityComparable* n) { n->ModifyOnThrow(); },
- absl::StrongGuarantee(factory, comp)));
+ auto nec_is_strong = [](NonEqualityComparable* nec) {
+ return testing::AssertionResult(nec->i == NonEqualityComparable().i);
+ };
+ auto strong_nec_tester = tester.WithInitialValue(NonEqualityComparable{})
+ .WithInvariants(nec_is_strong);
+
+ EXPECT_TRUE(strong_nec_tester.Test());
+ EXPECT_FALSE(strong_nec_tester.Test(
+ [](NonEqualityComparable* n) { n->ModifyOnThrow(); }));
}
template <typename T>
@@ -604,28 +718,32 @@ struct ExhaustivenessTester {
return true;
}
- friend testing::AssertionResult AbslCheckInvariants(
- ExhaustivenessTester*, absl::InternalAbslNamespaceFinder) {
+ static unsigned char successes;
+};
+
+struct {
+ template <typename T>
+ testing::AssertionResult operator()(ExhaustivenessTester<T>*) const {
return testing::AssertionSuccess();
}
+} CheckExhaustivenessTesterInvariants;
- static unsigned char successes;
-};
template <typename T>
unsigned char ExhaustivenessTester<T>::successes = 0;
TEST(ExceptionCheckTest, Exhaustiveness) {
- DefaultFactory<ExhaustivenessTester<int>> int_factory;
- EXPECT_TRUE(TestExceptionSafety(int_factory, CallOperator{}));
- EXPECT_EQ(ExhaustivenessTester<int>::successes, 0xF);
+ auto exhaust_tester = absl::MakeExceptionSafetyTester()
+ .WithInvariants(CheckExhaustivenessTesterInvariants)
+ .WithOperation(invoker);
- DefaultFactory<ExhaustivenessTester<ThrowingValue<>>> bomb_factory;
- EXPECT_TRUE(TestExceptionSafety(bomb_factory, CallOperator{}));
- EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF);
+ EXPECT_TRUE(
+ exhaust_tester.WithInitialValue(ExhaustivenessTester<int>{}).Test());
+ EXPECT_EQ(ExhaustivenessTester<int>::successes, 0xF);
- ExhaustivenessTester<ThrowingValue<>>::successes = 0;
- EXPECT_TRUE(TestExceptionSafety(bomb_factory, CallOperator{},
- StrongGuarantee(bomb_factory)));
+ EXPECT_TRUE(
+ exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
+ .WithInvariants(absl::strong_guarantee)
+ .Test());
EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF);
}
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h
index 87576df6..4bd273ed 100644
--- a/absl/base/internal/direct_mmap.h
+++ b/absl/base/internal/direct_mmap.h
@@ -67,7 +67,7 @@ namespace base_internal {
// Platform specific logic extracted from
// 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,
- off64_t offset) __THROW {
+ off64_t offset) noexcept {
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
(defined(__PPC__) && !defined(__PPC64__)) || \
@@ -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
// actual mmap()/munmap() methods.
+namespace absl {
+namespace base_internal {
+
inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
off_t offset) {
return mmap(start, length, prot, flags, fd, offset);
@@ -138,6 +141,9 @@ inline int DirectMunmap(void* start, size_t length) {
return munmap(start, length);
}
+} // namespace base_internal
+} // namespace absl
+
#endif // __linux__
#endif // ABSL_HAVE_MMAP
diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc
index 821438ec..c6f7c7cf 100644
--- a/absl/base/internal/exception_safety_testing.cc
+++ b/absl/base/internal/exception_safety_testing.cc
@@ -18,7 +18,10 @@
#include "absl/meta/type_traits.h"
namespace absl {
+
exceptions_internal::NoThrowTag no_throw_ctor;
+exceptions_internal::StrongGuaranteeTagType strong_guarantee;
+
namespace exceptions_internal {
int countdown = -1;
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index a0cd33b4..3493b5e2 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -23,6 +23,7 @@
#include <initializer_list>
#include <iosfwd>
#include <string>
+#include <tuple>
#include <unordered_map>
#include "gtest/gtest.h"
@@ -35,7 +36,6 @@
#include "absl/types/optional.h"
namespace absl {
-struct InternalAbslNamespaceFinder {};
struct ConstructorTracker;
@@ -63,6 +63,7 @@ constexpr NoThrow operator&(NoThrow a, NoThrow b) {
namespace exceptions_internal {
struct NoThrowTag {};
+struct StrongGuaranteeTagType {};
constexpr bool ThrowingAllowed(NoThrow flags, NoThrow flag) {
return !static_cast<bool>(flags & flag);
@@ -87,8 +88,7 @@ class TestException {
// bad_alloc exception in TestExceptionSafety.
class TestBadAllocException : public std::bad_alloc, public TestException {
public:
- explicit TestBadAllocException(absl::string_view msg)
- : TestException(msg) {}
+ explicit TestBadAllocException(absl::string_view msg) : TestException(msg) {}
using TestException::what;
};
@@ -128,75 +128,73 @@ class TrackedObject {
friend struct ::absl::ConstructorTracker;
};
-template <typename Factory>
-using FactoryType = typename absl::result_of_t<Factory()>::element_type;
-
-// Returns an optional with the result of the check if op fails, or an empty
-// optional if op passes
-template <typename Factory, typename Op, typename Checker>
-absl::optional<testing::AssertionResult> TestCheckerAtCountdown(
- Factory factory, const Op& op, int count, const Checker& check) {
+template <typename Factory, typename Operation, typename Invariant>
+absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
+ const Factory& factory, const Operation& operation, int count,
+ const Invariant& invariant) {
auto t_ptr = factory();
- absl::optional<testing::AssertionResult> out;
+ absl::optional<testing::AssertionResult> current_res;
+ exceptions_internal::countdown = count;
try {
- exceptions_internal::countdown = count;
- op(t_ptr.get());
+ operation(t_ptr.get());
} catch (const exceptions_internal::TestException& e) {
- out.emplace(check(t_ptr.get()));
- if (!*out) {
- *out << " caused by exception thrown by " << e.what();
+ current_res.emplace(invariant(t_ptr.get()));
+ if (!current_res.value()) {
+ *current_res << e.what() << " failed invariant check";
}
}
- return out;
+ exceptions_internal::countdown = -1;
+ return current_res;
+}
+
+template <typename Factory, typename Operation>
+absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
+ const Factory& factory, const Operation& operation, int count,
+ StrongGuaranteeTagType) {
+ using TPtr = typename decltype(factory())::pointer;
+ auto t_is_strong = [&](TPtr t) { return *t == *factory(); };
+ return TestSingleInvariantAtCountdownImpl(factory, operation, count,
+ t_is_strong);
}
-template <typename Factory, typename Op, typename Checker>
-int UpdateOut(Factory factory, const Op& op, int count, const Checker& checker,
- testing::AssertionResult* out) {
- if (*out) *out = *TestCheckerAtCountdown(factory, op, count, checker);
+template <typename Factory, typename Operation, typename Invariant>
+int TestSingleInvariantAtCountdown(
+ const Factory& factory, const Operation& operation, int count,
+ const Invariant& invariant,
+ absl::optional<testing::AssertionResult>* reduced_res) {
+ // If reduced_res is empty, it means the current call to
+ // TestSingleInvariantAtCountdown(...) is the first test being run so we do
+ // want to run it. Alternatively, if it's not empty (meaning a previous test
+ // has run) we want to check if it passed. If the previous test did pass, we
+ // want to contine running tests so we do want to run the current one. If it
+ // failed, we want to short circuit so as not to overwrite the AssertionResult
+ // output. If that's the case, we do not run the current test and instead we
+ // simply return.
+ if (!reduced_res->has_value() || reduced_res->value()) {
+ *reduced_res = TestSingleInvariantAtCountdownImpl(factory, operation, count,
+ invariant);
+ }
return 0;
}
-// Declare AbslCheckInvariants so that it can be found eventually via ADL.
-// Taking `...` gives it the lowest possible precedence.
-void AbslCheckInvariants(...);
-
-// Returns an optional with the result of the check if op fails, or an empty
-// optional if op passes
-template <typename Factory, typename Op, typename... Checkers>
-absl::optional<testing::AssertionResult> TestAtCountdown(
- Factory factory, const Op& op, int count, const Checkers&... checkers) {
- // Don't bother with the checkers if the class invariants are already broken.
- auto out = TestCheckerAtCountdown(
- factory, op, count, [](FactoryType<Factory>* t_ptr) {
- return AbslCheckInvariants(t_ptr, InternalAbslNamespaceFinder());
- });
- if (!out.has_value()) return out;
+template <typename Factory, typename Operation, typename... Invariants>
+inline absl::optional<testing::AssertionResult> TestAllInvariantsAtCountdown(
+ const Factory& factory, const Operation& operation, int count,
+ const Invariants&... invariants) {
+ absl::optional<testing::AssertionResult> reduced_res;
// Run each checker, short circuiting after the first failure
- int dummy[] = {0, (UpdateOut(factory, op, count, checkers, &*out))...};
+ int dummy[] = {
+ 0, (TestSingleInvariantAtCountdown(factory, operation, count, invariants,
+ &reduced_res))...};
static_cast<void>(dummy);
- return out;
+ return reduced_res;
}
-template <typename T, typename EqualTo>
-class StrongGuaranteeTester {
- public:
- explicit StrongGuaranteeTester(std::unique_ptr<T> t_ptr, EqualTo eq) noexcept
- : val_(std::move(t_ptr)), eq_(eq) {}
-
- testing::AssertionResult operator()(T* other) const {
- return eq_(*val_, *other) ? testing::AssertionSuccess()
- : testing::AssertionFailure() << "State changed";
- }
-
- private:
- std::unique_ptr<T> val_;
- EqualTo eq_;
-};
} // namespace exceptions_internal
extern exceptions_internal::NoThrowTag no_throw_ctor;
+extern exceptions_internal::StrongGuaranteeTagType strong_guarantee;
// These are useful for tests which just construct objects and make sure there
// are no leaks.
@@ -208,7 +206,7 @@ inline void UnsetCountdown() { exceptions_internal::countdown = -1; }
class ThrowingBool {
public:
ThrowingBool(bool b) noexcept : b_(b) {} // NOLINT(runtime/explicit)
- operator bool() const { // NOLINT(runtime/explicit)
+ operator bool() const { // NOLINT
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
return b_;
}
@@ -734,10 +732,9 @@ template <typename T, typename... Args>
T TestThrowingCtor(Args&&... args) {
struct Cleanup {
~Cleanup() { UnsetCountdown(); }
- };
- Cleanup c;
- for (int countdown = 0;; ++countdown) {
- exceptions_internal::countdown = countdown;
+ } c;
+ for (int count = 0;; ++count) {
+ exceptions_internal::countdown = count;
try {
return T(std::forward<Args>(args)...);
} catch (const exceptions_internal::TestException&) {
@@ -745,58 +742,237 @@ T TestThrowingCtor(Args&&... args) {
}
}
-// Tests that performing operation Op on a T follows exception safety
-// guarantees. By default only tests the basic guarantee. There must be a
-// function, AbslCheckInvariants(T*, absl::InternalAbslNamespaceFinder) which
-// returns anything convertible to bool and which makes sure the invariants of
-// the type are upheld. This is called before any of the checkers. The
-// InternalAbslNamespaceFinder is unused, and just helps find
-// AbslCheckInvariants for absl types which become aliases to std::types in
-// C++17.
-//
-// Parameters:
-// * TFactory: operator() returns a unique_ptr to the type under test (T). It
-// should always return pointers to values which compare equal.
-// * FunctionFromTPtrToVoid: A functor exercising the function under test. It
-// should take a T* and return void.
-// * Checkers: Any number of functions taking a T* and returning
-// anything contextually convertible to bool. If a testing::AssertionResult
-// is used then the error message is kept. These test invariants related to
-// the operation. To test the strong guarantee, pass
-// absl::StrongGuarantee(factory). A checker may freely modify the passed-in
-// T, for example to make sure the T can be set to a known state.
-template <typename TFactory, typename FunctionFromTPtrToVoid,
- typename... Checkers>
-testing::AssertionResult TestExceptionSafety(TFactory factory,
- FunctionFromTPtrToVoid&& op,
- const Checkers&... checkers) {
- struct Cleanup {
- ~Cleanup() { UnsetCountdown(); }
- } c;
- for (int countdown = 0;; ++countdown) {
- auto out = exceptions_internal::TestAtCountdown(factory, op, countdown,
- checkers...);
- if (!out.has_value()) {
- return testing::AssertionSuccess();
+namespace exceptions_internal {
+
+// Dummy struct for ExceptionSafetyTester<> partial state.
+struct UninitializedT {};
+
+template <typename T>
+class DefaultFactory {
+ public:
+ explicit DefaultFactory(const T& t) : t_(t) {}
+ std::unique_ptr<T> operator()() const { return absl::make_unique<T>(t_); }
+
+ private:
+ T t_;
+};
+
+template <size_t LazyInvariantsCount, typename LazyFactory,
+ typename LazyOperation>
+using EnableIfTestable = typename absl::enable_if_t<
+ LazyInvariantsCount != 0 &&
+ !std::is_same<LazyFactory, UninitializedT>::value &&
+ !std::is_same<LazyOperation, UninitializedT>::value>;
+
+template <typename Factory = UninitializedT,
+ typename Operation = UninitializedT, typename... Invariants>
+class ExceptionSafetyTester;
+
+} // namespace exceptions_internal
+
+exceptions_internal::ExceptionSafetyTester<> MakeExceptionSafetyTester();
+
+namespace exceptions_internal {
+
+/*
+ * Builds a tester object that tests if performing a operation on a T follows
+ * exception safety guarantees. Verification is done via invariant assertion
+ * callbacks applied to T instances post-throw.
+ *
+ * Template parameters for ExceptionSafetyTester:
+ *
+ * - Factory: The factory object (passed in via tester.WithFactory(...) or
+ * tester.WithInitialValue(...)) must be invocable with the signature
+ * `std::unique_ptr<T> operator()() const` where T is the type being tested.
+ * It is used for reliably creating identical T instances to test on.
+ *
+ * - Operation: The operation object (passsed in via tester.WithOperation(...)
+ * or tester.Test(...)) must be invocable with the signature
+ * `void operator()(T*) const` where T is the type being tested. It is used
+ * for performing steps on a T instance that may throw and that need to be
+ * checked for exception safety. Each call to the operation will receive a
+ * fresh T instance so it's free to modify and destroy the T instances as it
+ * pleases.
+ *
+ * - Invariants...: The invariant assertion callback objects (passed in via
+ * tester.WithInvariants(...)) must be invocable with the signature
+ * `testing::AssertionResult operator()(T*) const` where T is the type being
+ * tested. Invariant assertion callbacks are provided T instances post-throw.
+ * They must return testing::AssertionSuccess when the type invariants of the
+ * provided T instance hold. If the type invariants of the T instance do not
+ * hold, they must return testing::AssertionFailure. Execution order of
+ * Invariants... is unspecified. They will each individually get a fresh T
+ * instance so they are free to modify and destroy the T instances as they
+ * please.
+ */
+template <typename Factory, typename Operation, typename... Invariants>
+class ExceptionSafetyTester {
+ public:
+ /*
+ * Returns a new ExceptionSafetyTester with an included T factory based on the
+ * provided T instance. The existing factory will not be included in the newly
+ * created tester instance. The created factory returns a new T instance by
+ * copy-constructing the provided const T& t.
+ *
+ * Preconditions for tester.WithInitialValue(const T& t):
+ *
+ * - The const T& t object must be copy-constructible where T is the type
+ * being tested. For non-copy-constructible objects, use the method
+ * tester.WithFactory(...).
+ */
+ template <typename T>
+ ExceptionSafetyTester<DefaultFactory<T>, Operation, Invariants...>
+ WithInitialValue(const T& t) const {
+ return WithFactory(DefaultFactory<T>(t));
+ }
+
+ /*
+ * Returns a new ExceptionSafetyTester with the provided T factory included.
+ * The existing factory will not be included in the newly-created tester
+ * instance. This method is intended for use with types lacking a copy
+ * constructor. Types that can be copy-constructed should instead use the
+ * method tester.WithInitialValue(...).
+ */
+ template <typename NewFactory>
+ ExceptionSafetyTester<absl::decay_t<NewFactory>, Operation, Invariants...>
+ WithFactory(const NewFactory& new_factory) const {
+ return {new_factory, operation_, invariants_};
+ }
+
+ /*
+ * Returns a new ExceptionSafetyTester with the provided testable operation
+ * included. The existing operation will not be included in the newly created
+ * tester.
+ */
+ template <typename NewOperation>
+ ExceptionSafetyTester<Factory, absl::decay_t<NewOperation>, Invariants...>
+ WithOperation(const NewOperation& new_operation) const {
+ return {factory_, new_operation, invariants_};
+ }
+
+ /*
+ * Returns a new ExceptionSafetyTester with the provided MoreInvariants...
+ * combined with the Invariants... that were already included in the instance
+ * on which the method was called. Invariants... cannot be removed or replaced
+ * once added to an ExceptionSafetyTester instance. A fresh object must be
+ * created in order to get an empty Invariants... list.
+ *
+ * In addition to passing in custom invariant assertion callbacks, this method
+ * accepts `absl::strong_guarantee` as an argument which checks T instances
+ * post-throw against freshly created T instances via operator== to verify
+ * that any state changes made during the execution of the operation were
+ * properly rolled back.
+ */
+ template <typename... MoreInvariants>
+ ExceptionSafetyTester<Factory, Operation, Invariants...,
+ absl::decay_t<MoreInvariants>...>
+ WithInvariants(const MoreInvariants&... more_invariants) const {
+ return {factory_, operation_,
+ std::tuple_cat(invariants_,
+ std::tuple<absl::decay_t<MoreInvariants>...>(
+ more_invariants...))};
+ }
+
+ /*
+ * Returns a testing::AssertionResult that is the reduced result of the
+ * exception safety algorithm. The algorithm short circuits and returns
+ * AssertionFailure after the first invariant callback returns an
+ * AssertionFailure. Otherwise, if all invariant callbacks return an
+ * AssertionSuccess, the reduced result is AssertionSuccess.
+ *
+ * The passed-in testable operation will not be saved in a new tester instance
+ * nor will it modify/replace the existing tester instance. This is useful
+ * when each operation being tested is unique and does not need to be reused.
+ *
+ * Preconditions for tester.Test(const NewOperation& new_operation):
+ *
+ * - May only be called after at least one invariant assertion callback and a
+ * factory or initial value have been provided.
+ */
+ template <
+ typename NewOperation,
+ typename = EnableIfTestable<sizeof...(Invariants), Factory, NewOperation>>
+ testing::AssertionResult Test(const NewOperation& new_operation) const {
+ return TestImpl(new_operation, absl::index_sequence_for<Invariants...>());
+ }
+
+ /*
+ * Returns a testing::AssertionResult that is the reduced result of the
+ * exception safety algorithm. The algorithm short circuits and returns
+ * AssertionFailure after the first invariant callback returns an
+ * AssertionFailure. Otherwise, if all invariant callbacks return an
+ * AssertionSuccess, the reduced result is AssertionSuccess.
+ *
+ * Preconditions for tester.Test():
+ *
+ * - May only be called after at least one invariant assertion callback, a
+ * factory or initial value and a testable operation have been provided.
+ */
+ template <typename LazyOperation = Operation,
+ typename =
+ EnableIfTestable<sizeof...(Invariants), Factory, LazyOperation>>
+ testing::AssertionResult Test() const {
+ return TestImpl(operation_, absl::index_sequence_for<Invariants...>());
+ }
+
+ private:
+ template <typename, typename, typename...>
+ friend class ExceptionSafetyTester;
+
+ friend ExceptionSafetyTester<> absl::MakeExceptionSafetyTester();
+
+ ExceptionSafetyTester() {}
+
+ ExceptionSafetyTester(const Factory& f, const Operation& o,
+ const std::tuple<Invariants...>& i)
+ : factory_(f), operation_(o), invariants_(i) {}
+
+ template <typename SelectedOperation, size_t... Indices>
+ testing::AssertionResult TestImpl(const SelectedOperation& selected_operation,
+ absl::index_sequence<Indices...>) const {
+ // Starting from 0 and counting upwards until one of the exit conditions is
+ // hit...
+ for (int count = 0;; ++count) {
+ // Run the full exception safety test algorithm for the current countdown
+ auto reduced_res =
+ TestAllInvariantsAtCountdown(factory_, selected_operation, count,
+ std::get<Indices>(invariants_)...);
+ // If there is no value in the optional, no invariants were run because no
+ // exception was thrown. This means that the test is complete and the loop
+ // can exit successfully.
+ if (!reduced_res.has_value()) {
+ return testing::AssertionSuccess();
+ }
+ // If the optional is not empty and the value is falsy, an invariant check
+ // failed so the test must exit to propegate the failure.
+ if (!reduced_res.value()) {
+ return reduced_res.value();
+ }
+ // If the optional is not empty and the value is not falsy, it means
+ // exceptions were thrown but the invariants passed so the test must
+ // continue to run.
}
- if (!*out) return *out;
}
-}
-// Returns a functor to test for the strong exception-safety guarantee.
-// Equality comparisons are made against the T provided by the factory and
-// default to using operator==.
-//
-// Parameters:
-// * TFactory: operator() returns a unique_ptr to the type under test. It
-// should always return pointers to values which compare equal.
-template <typename TFactory, typename EqualTo = std::equal_to<
- exceptions_internal::FactoryType<TFactory>>>
-exceptions_internal::StrongGuaranteeTester<
- exceptions_internal::FactoryType<TFactory>, EqualTo>
-StrongGuarantee(TFactory factory, EqualTo eq = EqualTo()) {
- return exceptions_internal::StrongGuaranteeTester<
- exceptions_internal::FactoryType<TFactory>, EqualTo>(factory(), eq);
+ Factory factory_;
+ Operation operation_;
+ std::tuple<Invariants...> invariants_;
+};
+
+} // namespace exceptions_internal
+
+/*
+ * Constructs an empty ExceptionSafetyTester. All ExceptionSafetyTester
+ * objects are immutable and all With[thing] mutation methods return new
+ * instances of ExceptionSafetyTester.
+ *
+ * In order to test a T for exception safety, a factory for that T, a testable
+ * operation, and at least one invariant callback returning an assertion
+ * result must be applied using the respective methods.
+ */
+inline exceptions_internal::ExceptionSafetyTester<>
+MakeExceptionSafetyTester() {
+ return {};
}
} // namespace absl
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 5f047a54..0a6f3070 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -23,6 +23,7 @@
#include "absl/base/call_once.h"
#include "absl/base/config.h"
+#include "absl/base/internal/direct_mmap.h"
#include "absl/base/internal/scheduling_mode.h"
#include "absl/base/macros.h"
#include "absl/base/thread_annotations.h"
@@ -49,8 +50,6 @@
#include <new> // for placement-new
#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/spinlock.h"
@@ -405,7 +404,7 @@ bool LowLevelAlloc::DeleteArena(Arena *arena) {
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
munmap_result = munmap(region, size);
} else {
- munmap_result = MallocHook::UnhookedMUnmap(region, size);
+ munmap_result = base_internal::DirectMunmap(region, size);
}
if (munmap_result != 0) {
ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d",
@@ -503,9 +502,6 @@ void LowLevelAlloc::Free(void *v) {
ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
"bad magic number in Free()");
LowLevelAlloc::Arena *arena = f->header.arena;
- if ((arena->flags & kCallMallocHook) != 0) {
- MallocHook::InvokeDeleteHook(v);
- }
ArenaLock section(arena);
AddToFreelist(v, arena);
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) {
ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed");
#else
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);
} else {
new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
@@ -593,21 +589,12 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
void *LowLevelAlloc::Alloc(size_t request) {
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;
}
void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) {
ABSL_RAW_CHECK(arena != nullptr, "must pass a valid 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;
}
diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc
index 7c359f30..cf2b3632 100644
--- a/absl/base/internal/low_level_alloc_test.cc
+++ b/absl/base/internal/low_level_alloc_test.cc
@@ -21,8 +21,6 @@
#include <unordered_map>
#include <utility>
-#include "absl/base/internal/malloc_hook.h"
-
namespace absl {
namespace base_internal {
namespace {
@@ -139,58 +137,12 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) {
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().
static struct 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_ASSERT(allocates != 0); // default arena calls hooks
- TEST_ASSERT(frees != 0);
- 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));
+ Test(true, false, 50000);
+ Test(true, true, 50000);
}
} before_main;
diff --git a/absl/base/internal/malloc_extension.cc b/absl/base/internal/malloc_extension.cc
deleted file mode 100644
index 5b906533..00000000
--- a/absl/base/internal/malloc_extension.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// 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;
-}
diff --git a/absl/base/internal/malloc_extension.h b/absl/base/internal/malloc_extension.h
deleted file mode 100644
index 75a00ce9..00000000
--- a/absl/base/internal/malloc_extension.h
+++ /dev/null
@@ -1,426 +0,0 @@
-//
-// 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.
-//
-
-// Extra extensions exported by some malloc implementations. These
-// extensions are accessed through a virtual base class so an
-// application can link against a malloc that does not implement these
-// extensions, and it will get default versions that do nothing.
-//
-// NOTE FOR C USERS: If you wish to use this functionality from within
-// a C program, see malloc_extension_c.h.
-
-#ifndef ABSL_BASE_INTERNAL_MALLOC_EXTENSION_H_
-#define ABSL_BASE_INTERNAL_MALLOC_EXTENSION_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <atomic>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "absl/base/attributes.h"
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-namespace absl {
-namespace base_internal {
-
-class MallocExtensionWriter;
-
-// Interface to a pluggable system allocator.
-class SysAllocator {
- public:
- SysAllocator() {
- }
- virtual ~SysAllocator();
-
- // Allocates "size"-byte of memory from system aligned with "alignment".
- // Returns null if failed. Otherwise, the returned pointer p up to and
- // including (p + actual_size -1) have been allocated.
- virtual void* Alloc(size_t size, size_t *actual_size, size_t alignment) = 0;
-
- // Get a human-readable description of the current state of the
- // allocator. The state is stored as a null-terminated std::string in
- // a prefix of buffer.
- virtual void GetStats(char* buffer, int length);
-};
-
-// The default implementations of the following routines do nothing.
-// All implementations should be thread-safe; the current ones
-// (DebugMallocImplementation and TCMallocImplementation) are.
-class MallocExtension {
- public:
- virtual ~MallocExtension();
-
- // Verifies that all blocks are valid. Returns true if all are; dumps
- // core otherwise. A no-op except in debug mode. Even in debug mode,
- // they may not do any checking except with certain malloc
- // implementations. Thread-safe.
- virtual bool VerifyAllMemory();
-
- // Verifies that p was returned by new, has not been deleted, and is
- // valid. Returns true if p is good; dumps core otherwise. A no-op
- // except in debug mode. Even in debug mode, may not do any checking
- // except with certain malloc implementations. Thread-safe.
- virtual bool VerifyNewMemory(const void* p);
-
- // Verifies that p was returned by new[], has not been deleted, and is
- // valid. Returns true if p is good; dumps core otherwise. A no-op
- // except in debug mode. Even in debug mode, may not do any checking
- // except with certain malloc implementations. Thread-safe.
- virtual bool VerifyArrayNewMemory(const void* p);
-
- // Verifies that p was returned by malloc, has not been freed, and is
- // valid. Returns true if p is good; dumps core otherwise. A no-op
- // except in debug mode. Even in debug mode, may not do any checking
- // except with certain malloc implementations. Thread-safe.
- virtual bool VerifyMallocMemory(const void* p);
-
- // If statistics collection is enabled, sets *blocks to be the number of
- // currently allocated blocks, sets *total to be the total size allocated
- // over all blocks, sets histogram[n] to be the number of blocks with
- // size between 2^n-1 and 2^(n+1), and returns true. Returns false, and
- // does not change *blocks, *total, or *histogram, if statistics
- // collection is disabled.
- //
- // Note that these statistics reflect memory allocated by new, new[],
- // malloc(), and realloc(), but not mmap(). They may be larger (if not
- // all pages have been written to) or smaller (if pages have been
- // allocated by mmap()) than the total RSS size. They will always be
- // smaller than the total virtual memory size.
- static constexpr int kMallocHistogramSize = 64;
- virtual bool MallocMemoryStats(int* blocks, size_t* total,
- int histogram[kMallocHistogramSize]);
-
- // Get a human readable description of the current state of the malloc
- // data structures. The state is stored as a null-terminated std::string
- // in a prefix of "buffer[0,buffer_length-1]".
- // REQUIRES: buffer_length > 0.
- virtual void GetStats(char* buffer, int buffer_length);
-
- // Outputs to "writer" a sample of live objects and the stack traces
- // that allocated these objects. The output can be passed to pprof.
- virtual void GetHeapSample(MallocExtensionWriter* writer);
-
- // Outputs to "writer" the stack traces that caused growth in the
- // address space size. The output can be passed to "pprof".
- virtual void GetHeapGrowthStacks(MallocExtensionWriter* writer);
-
- // Outputs to "writer" a fragmentation profile. The output can be
- // passed to "pprof". In particular, the result is a list of
- // <n,total,stacktrace> tuples that says that "total" bytes in "n"
- // objects are currently unusable because of fragmentation caused by
- // an allocation with the specified "stacktrace".
- virtual void GetFragmentationProfile(MallocExtensionWriter* writer);
-
- // -------------------------------------------------------------------
- // Control operations for getting and setting malloc implementation
- // specific parameters. Some currently useful properties:
- //
- // generic
- // -------
- // "generic.current_allocated_bytes"
- // Number of bytes currently allocated by application
- // This property is not writable.
- //
- // "generic.heap_size"
- // Number of bytes in the heap ==
- // current_allocated_bytes +
- // fragmentation +
- // freed memory regions
- // This property is not writable.
- //
- // tcmalloc
- // --------
- // "tcmalloc.max_total_thread_cache_bytes"
- // Upper limit on total number of bytes stored across all
- // per-thread caches. Default: 16MB.
- //
- // "tcmalloc.current_total_thread_cache_bytes"
- // Number of bytes used across all thread caches.
- // This property is not writable.
- //
- // "tcmalloc.pageheap_free_bytes"
- // Number of bytes in free, mapped pages in page heap. These
- // bytes can be used to fulfill allocation requests. They
- // always count towards virtual memory usage, and unless the
- // underlying memory is swapped out by the OS, they also count
- // towards physical memory usage. This property is not writable.
- //
- // "tcmalloc.pageheap_unmapped_bytes"
- // Number of bytes in free, unmapped pages in page heap.
- // These are bytes that have been released back to the OS,
- // possibly by one of the MallocExtension "Release" calls.
- // They can be used to fulfill allocation requests, but
- // typically incur a page fault. They always count towards
- // virtual memory usage, and depending on the OS, typically
- // do not count towards physical memory usage. This property
- // is not writable.
- //
- // "tcmalloc.per_cpu_caches_active"
- // Whether tcmalloc is using per-CPU caches (1 or 0 respectively).
- // This property is not writable.
- // -------------------------------------------------------------------
-
- // Get the named "property"'s value. Returns true if the property
- // is known. Returns false if the property is not a valid property
- // name for the current malloc implementation.
- // REQUIRES: property != null; value != null
- virtual bool GetNumericProperty(const char* property, size_t* value);
-
- // Set the named "property"'s value. Returns true if the property
- // is known and writable. Returns false if the property is not a
- // valid property name for the current malloc implementation, or
- // is not writable.
- // REQUIRES: property != null
- virtual bool SetNumericProperty(const char* property, size_t value);
-
- // Mark the current thread as "idle". This routine may optionally
- // be called by threads as a hint to the malloc implementation that
- // any thread-specific resources should be released. Note: this may
- // be an expensive routine, so it should not be called too often.
- //
- // Also, if the code that calls this routine will go to sleep for
- // a while, it should take care to not allocate anything between
- // the call to this routine and the beginning of the sleep.
- //
- // Most malloc implementations ignore this routine.
- virtual void MarkThreadIdle();
-
- // Mark the current thread as "busy". This routine should be
- // called after MarkThreadIdle() if the thread will now do more
- // work. If this method is not called, performance may suffer.
- //
- // Most malloc implementations ignore this routine.
- virtual void MarkThreadBusy();
-
- // Attempt to free any resources associated with cpu <cpu> (in the sense
- // of only being usable from that CPU.) Returns the number of bytes
- // previously assigned to "cpu" that were freed. Safe to call from
- // any processor, not just <cpu>.
- //
- // Most malloc implementations ignore this routine (known exceptions:
- // tcmalloc with --tcmalloc_per_cpu_caches=true.)
- virtual size_t ReleaseCPUMemory(int cpu);
-
- // Gets the system allocator used by the malloc extension instance. Returns
- // null for malloc implementations that do not support pluggable system
- // allocators.
- virtual SysAllocator* GetSystemAllocator();
-
- // Sets the system allocator to the specified.
- //
- // Users could register their own system allocators for malloc implementation
- // that supports pluggable system allocators, such as TCMalloc, by doing:
- // alloc = new MyOwnSysAllocator();
- // MallocExtension::instance()->SetSystemAllocator(alloc);
- // It's up to users whether to fall back (recommended) to the default
- // system allocator (use GetSystemAllocator() above) or not. The caller is
- // responsible to any necessary locking.
- // See tcmalloc/system-alloc.h for the interface and
- // tcmalloc/memfs_malloc.cc for the examples.
- //
- // It's a no-op for malloc implementations that do not support pluggable
- // system allocators.
- virtual void SetSystemAllocator(SysAllocator *a);
-
- // Try to release num_bytes of free memory back to the operating
- // system for reuse. Use this extension with caution -- to get this
- // memory back may require faulting pages back in by the OS, and
- // that may be slow. (Currently only implemented in tcmalloc.)
- virtual void ReleaseToSystem(size_t num_bytes);
-
- // Same as ReleaseToSystem() but release as much memory as possible.
- virtual void ReleaseFreeMemory();
-
- // Sets the rate at which we release unused memory to the system.
- // Zero means we never release memory back to the system. Increase
- // this flag to return memory faster; decrease it to return memory
- // slower. Reasonable rates are in the range [0,10]. (Currently
- // only implemented in tcmalloc).
- virtual void SetMemoryReleaseRate(double rate);
-
- // Gets the release rate. Returns a value < 0 if unknown.
- virtual double GetMemoryReleaseRate();
-
- // Returns the estimated number of bytes that will be allocated for
- // a request of "size" bytes. This is an estimate: an allocation of
- // SIZE bytes may reserve more bytes, but will never reserve less.
- // (Currently only implemented in tcmalloc, other implementations
- // always return SIZE.)
- // This is equivalent to malloc_good_size() in OS X.
- virtual size_t GetEstimatedAllocatedSize(size_t size);
-
- // Returns the actual number N of bytes reserved by tcmalloc for the
- // pointer p. This number may be equal to or greater than the
- // number of bytes requested when p was allocated.
- //
- // This routine is just useful for statistics collection. The
- // client must *not* read or write from the extra bytes that are
- // indicated by this call.
- //
- // Example, suppose the client gets memory by calling
- // p = malloc(10)
- // and GetAllocatedSize(p) returns 16. The client must only use the
- // first 10 bytes p[0..9], and not attempt to read or write p[10..15].
- //
- // p must have been allocated by this malloc implementation, must
- // not be an interior pointer -- that is, must be exactly the
- // pointer returned to by malloc() et al., not some offset from that
- // -- and should not have been freed yet. p may be null.
- // (Currently only implemented in tcmalloc; other implementations
- // will return 0.)
- virtual size_t GetAllocatedSize(const void* p);
-
- // Returns kOwned if this malloc implementation allocated the memory
- // pointed to by p, or kNotOwned if some other malloc implementation
- // allocated it or p is null. May also return kUnknownOwnership if
- // the malloc implementation does not keep track of ownership.
- // REQUIRES: p must be a value returned from a previous call to
- // malloc(), calloc(), realloc(), memalign(), posix_memalign(),
- // valloc(), pvalloc(), new, or new[], and must refer to memory that
- // is currently allocated (so, for instance, you should not pass in
- // a pointer after having called free() on it).
- enum Ownership {
- // NOTE: Enum values MUST be kept in sync with the version in
- // malloc_extension_c.h
- kUnknownOwnership = 0,
- kOwned,
- kNotOwned
- };
- virtual Ownership GetOwnership(const void* p);
-
- // The current malloc implementation. Always non-null.
- static MallocExtension* instance() {
- InitModuleOnce();
- return current_instance_.load(std::memory_order_acquire);
- }
-
- // Change the malloc implementation. Typically called by the
- // malloc implementation during initialization.
- static void Register(MallocExtension* implementation);
-
- // Type used by GetProperties. See comment on GetProperties.
- struct Property {
- size_t value;
- // Stores breakdown of the property value bucketed by object size.
- struct Bucket {
- size_t min_object_size;
- size_t max_object_size;
- size_t size;
- };
- // Empty unless detailed info was asked for and this type has buckets
- std::vector<Bucket> buckets;
- };
-
- // Type used by GetProperties. See comment on GetProperties.
- enum StatLevel { kSummary, kDetailed };
-
- // Stores in *result detailed statistics about the malloc
- // implementation. *result will be a map keyed by the name of
- // the statistic. Each statistic has at least a "value" field.
- //
- // Some statistics may also contain an array of buckets if
- // level==kDetailed and the "value" can be subdivided
- // into different buckets for different object sizes. If
- // such detailed statistics are not available, Property::buckets
- // will be empty. Otherwise Property::buckets will contain
- // potentially many entries. For each bucket b, b.value
- // will count the value contributed by objects in the range
- // [b.min_object_size, b.max_object_size].
- //
- // Common across malloc implementations:
- // generic.bytes_in_use_by_app -- Bytes currently in use by application
- // generic.physical_memory_used -- Overall (including malloc internals)
- // generic.virtual_memory_used -- Overall (including malloc internals)
- //
- // Tcmalloc specific properties
- // tcmalloc.cpu_free -- Bytes in per-cpu free-lists
- // tcmalloc.thread_cache_free -- Bytes in per-thread free-lists
- // tcmalloc.transfer_cache -- Bytes in cross-thread transfer caches
- // tcmalloc.central_cache_free -- Bytes in central cache
- // tcmalloc.page_heap_free -- Bytes in page heap
- // tcmalloc.page_heap_unmapped -- Bytes in page heap (no backing phys. mem)
- // tcmalloc.metadata_bytes -- Used by internal data structures
- // tcmalloc.thread_cache_count -- Number of thread caches in use
- //
- // Debug allocator
- // debug.free_queue -- Recently freed objects
- virtual void GetProperties(StatLevel level,
- std::map<std::string, Property>* result);
- private:
- static MallocExtension* InitModule();
-
- static void InitModuleOnce() {
- // Pointer stored here so heap leak checker will consider the default
- // instance reachable, even if current_instance_ is later overridden by
- // MallocExtension::Register().
- ABSL_ATTRIBUTE_UNUSED static MallocExtension* default_instance =
- InitModule();
- }
-
- static std::atomic<MallocExtension*> current_instance_;
-};
-
-// Base class than can handle output generated by GetHeapSample() and
-// GetHeapGrowthStacks(). Use the available subclass or roll your
-// own. Useful if you want explicit control over the type of output
-// buffer used (e.g. IOBuffer, Cord, etc.)
-class MallocExtensionWriter {
- public:
- virtual ~MallocExtensionWriter() {}
- virtual void Write(const char* buf, int len) = 0;
- protected:
- MallocExtensionWriter() {}
- MallocExtensionWriter(const MallocExtensionWriter&) = delete;
- MallocExtensionWriter& operator=(const MallocExtensionWriter&) = delete;
-
- private:
- virtual void UnusedKeyMethod(); // Dummy key method to avoid weak vtable.
-};
-
-// A subclass that writes to the std::string "out". NOTE: The generated
-// data is *appended* to "*out". I.e., the old contents of "*out" are
-// preserved.
-class StringMallocExtensionWriter : public MallocExtensionWriter {
- public:
- explicit StringMallocExtensionWriter(std::string* out) : out_(out) {}
- void Write(const char* buf, int len) override;
-
- private:
- std::string* const out_;
- StringMallocExtensionWriter(const StringMallocExtensionWriter&) = delete;
- StringMallocExtensionWriter& operator=(const StringMallocExtensionWriter&) =
- delete;
-};
-
-} // namespace base_internal
-} // namespace absl
-
-// The nallocx function allocates no memory, but it performs the same size
-// computation as the malloc function, and returns the real size of the
-// allocation that would result from the equivalent malloc function call.
-// Default weak implementation returns size unchanged, but tcmalloc overrides it
-// and returns rounded up size. See the following link for details:
-// http://www.unix.com/man-page/freebsd/3/nallocx/
-extern "C" size_t nallocx(size_t size, int flags);
-
-#ifndef MALLOCX_LG_ALIGN
-#define MALLOCX_LG_ALIGN(la) (la)
-#endif
-
-#endif // ABSL_BASE_INTERNAL_MALLOC_EXTENSION_H_
diff --git a/absl/base/internal/malloc_extension_c.h b/absl/base/internal/malloc_extension_c.h
deleted file mode 100644
index 5afc84a4..00000000
--- a/absl/base/internal/malloc_extension_c.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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_ */
diff --git a/absl/base/internal/malloc_extension_test.cc b/absl/base/internal/malloc_extension_test.cc
deleted file mode 100644
index 76605eb1..00000000
--- a/absl/base/internal/malloc_extension_test.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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
diff --git a/absl/base/internal/malloc_hook.cc b/absl/base/internal/malloc_hook.cc
deleted file mode 100644
index c364d3ff..00000000
--- a/absl/base/internal/malloc_hook.cc
+++ /dev/null
@@ -1,574 +0,0 @@
-// 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/attributes.h"
-#include "absl/base/config.h"
-
-#if ABSL_HAVE_MMAP
-// Disable the glibc prototype of mremap(), as older versions of the
-// system headers define this function with only four arguments,
-// whereas newer versions allow an optional fifth argument:
-#define mremap glibc_mremap
-#include <sys/mman.h>
-#undef mremap
-#endif
-
-#include "absl/base/internal/malloc_hook.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-
-#include "absl/base/call_once.h"
-#include "absl/base/internal/malloc_hook_invoke.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/macros.h"
-
-// __THROW is defined in glibc systems. It means, counter-intuitively,
-// "This function will never throw an exception." It's an optional
-// optimization tool, but we may need to use it to match glibc prototypes.
-#ifndef __THROW // I guess we're not on a glibc system
-# define __THROW // __THROW is just an optimization, so ok to make it ""
-#endif
-
-namespace absl {
-namespace base_internal {
-namespace {
-
-void RemoveInitialHooksAndCallInitializers(); // below.
-
-absl::once_flag once;
-
-// These hooks are installed in MallocHook as the only initial hooks. The first
-// hook that is called will run RemoveInitialHooksAndCallInitializers (see the
-// definition below) and then redispatch to any malloc hooks installed by
-// RemoveInitialHooksAndCallInitializers.
-//
-// Note(llib): there is a possibility of a race in the event that there are
-// multiple threads running before the first allocation. This is pretty
-// difficult to achieve, but if it is then multiple threads may concurrently do
-// allocations. The first caller will call
-// RemoveInitialHooksAndCallInitializers via one of the initial hooks. A
-// concurrent allocation may, depending on timing either:
-// * still have its initial malloc hook installed, run that and block on waiting
-// for the first caller to finish its call to
-// RemoveInitialHooksAndCallInitializers, and proceed normally.
-// * occur some time during the RemoveInitialHooksAndCallInitializers call, at
-// which point there could be no initial hooks and the subsequent hooks that
-// are about to be set up by RemoveInitialHooksAndCallInitializers haven't
-// been installed yet. I think the worst we can get is that some allocations
-// will not get reported to some hooks set by the initializers called from
-// RemoveInitialHooksAndCallInitializers.
-
-void InitialNewHook(const void* ptr, size_t size) {
- absl::call_once(once, RemoveInitialHooksAndCallInitializers);
- MallocHook::InvokeNewHook(ptr, size);
-}
-
-void InitialPreMMapHook(const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset) {
- absl::call_once(once, RemoveInitialHooksAndCallInitializers);
- MallocHook::InvokePreMmapHook(start, size, protection, flags, fd, offset);
-}
-
-void InitialPreSbrkHook(ptrdiff_t increment) {
- absl::call_once(once, RemoveInitialHooksAndCallInitializers);
- MallocHook::InvokePreSbrkHook(increment);
-}
-
-// This function is called at most once by one of the above initial malloc
-// hooks. It removes all initial hooks and initializes all other clients that
-// want to get control at the very first memory allocation. The initializers
-// may assume that the initial malloc hooks have been removed. The initializers
-// may set up malloc hooks and allocate memory.
-void RemoveInitialHooksAndCallInitializers() {
- ABSL_RAW_CHECK(MallocHook::RemoveNewHook(&InitialNewHook), "");
- ABSL_RAW_CHECK(MallocHook::RemovePreMmapHook(&InitialPreMMapHook), "");
- ABSL_RAW_CHECK(MallocHook::RemovePreSbrkHook(&InitialPreSbrkHook), "");
-}
-
-} // namespace
-} // namespace base_internal
-} // namespace absl
-
-namespace absl {
-namespace base_internal {
-
-// This lock is shared between all implementations of HookList::Add & Remove.
-// The potential for contention is very small. This needs to be a SpinLock and
-// not a Mutex since it's possible for Mutex locking to allocate memory (e.g.,
-// per-thread allocation in debug builds), which could cause infinite recursion.
-static absl::base_internal::SpinLock hooklist_spinlock(
- absl::base_internal::kLinkerInitialized);
-
-template <typename T>
-bool HookList<T>::Add(T value_as_t) {
- if (value_as_t == T()) {
- return false;
- }
- absl::base_internal::SpinLockHolder l(&hooklist_spinlock);
- // Find the first slot in data that is 0.
- int index = 0;
- while ((index < kHookListMaxValues) &&
- (priv_data[index].load(std::memory_order_relaxed) != 0)) {
- ++index;
- }
- if (index == kHookListMaxValues) {
- return false;
- }
- int prev_num_hooks = priv_end.load(std::memory_order_acquire);
- priv_data[index].store(reinterpret_cast<intptr_t>(value_as_t),
- std::memory_order_release);
- if (prev_num_hooks <= index) {
- priv_end.store(index + 1, std::memory_order_release);
- }
- return true;
-}
-
-template <typename T>
-bool HookList<T>::Remove(T value_as_t) {
- if (value_as_t == T()) {
- return false;
- }
- absl::base_internal::SpinLockHolder l(&hooklist_spinlock);
- int hooks_end = priv_end.load(std::memory_order_acquire);
- int index = 0;
- while (index < hooks_end &&
- value_as_t != reinterpret_cast<T>(
- priv_data[index].load(std::memory_order_acquire))) {
- ++index;
- }
- if (index == hooks_end) {
- return false;
- }
- priv_data[index].store(0, std::memory_order_release);
- if (hooks_end == index + 1) {
- // Adjust hooks_end down to the lowest possible value.
- hooks_end = index;
- while ((hooks_end > 0) &&
- (priv_data[hooks_end - 1].load(std::memory_order_acquire) == 0)) {
- --hooks_end;
- }
- priv_end.store(hooks_end, std::memory_order_release);
- }
- return true;
-}
-
-template <typename T>
-int HookList<T>::Traverse(T* output_array, int n) const {
- int hooks_end = priv_end.load(std::memory_order_acquire);
- int actual_hooks_end = 0;
- for (int i = 0; i < hooks_end && n > 0; ++i) {
- T data = reinterpret_cast<T>(priv_data[i].load(std::memory_order_acquire));
- if (data != T()) {
- *output_array++ = data;
- ++actual_hooks_end;
- --n;
- }
- }
- return actual_hooks_end;
-}
-
-// Initialize a HookList (optionally with the given initial_value in index 0).
-#define INIT_HOOK_LIST { {0}, {{}} }
-#define INIT_HOOK_LIST_WITH_VALUE(initial_value) \
- { {1}, { {reinterpret_cast<intptr_t>(initial_value)} } }
-
-// Explicit instantiation for malloc_hook_test.cc. This ensures all the methods
-// are instantiated.
-template struct HookList<MallocHook::NewHook>;
-
-HookList<MallocHook::NewHook> new_hooks_ =
- INIT_HOOK_LIST_WITH_VALUE(&InitialNewHook);
-HookList<MallocHook::DeleteHook> delete_hooks_ = INIT_HOOK_LIST;
-HookList<MallocHook::SampledNewHook> sampled_new_hooks_ = INIT_HOOK_LIST;
-HookList<MallocHook::SampledDeleteHook> sampled_delete_hooks_ = INIT_HOOK_LIST;
-HookList<MallocHook::PreMmapHook> premmap_hooks_ =
- INIT_HOOK_LIST_WITH_VALUE(&InitialPreMMapHook);
-HookList<MallocHook::MmapHook> mmap_hooks_ = INIT_HOOK_LIST;
-HookList<MallocHook::MunmapHook> munmap_hooks_ = INIT_HOOK_LIST;
-HookList<MallocHook::MremapHook> mremap_hooks_ = INIT_HOOK_LIST;
-HookList<MallocHook::PreSbrkHook> presbrk_hooks_ =
- INIT_HOOK_LIST_WITH_VALUE(InitialPreSbrkHook);
-HookList<MallocHook::SbrkHook> sbrk_hooks_ = INIT_HOOK_LIST;
-
-// These lists contain either 0 or 1 hooks.
-HookList<MallocHook::MmapReplacement> mmap_replacement_ = INIT_HOOK_LIST;
-HookList<MallocHook::MunmapReplacement> munmap_replacement_ = INIT_HOOK_LIST;
-
-#undef INIT_HOOK_LIST_WITH_VALUE
-#undef INIT_HOOK_LIST
-
-bool MallocHook::AddNewHook(NewHook hook) { return new_hooks_.Add(hook); }
-
-bool MallocHook::RemoveNewHook(NewHook hook) { return new_hooks_.Remove(hook); }
-
-bool MallocHook::AddDeleteHook(DeleteHook hook) {
- return delete_hooks_.Add(hook);
-}
-
-bool MallocHook::RemoveDeleteHook(DeleteHook hook) {
- return delete_hooks_.Remove(hook);
-}
-
-bool MallocHook::AddSampledNewHook(SampledNewHook hook) {
- return sampled_new_hooks_.Add(hook);
-}
-
-bool MallocHook::RemoveSampledNewHook(SampledNewHook hook) {
- return sampled_new_hooks_.Remove(hook);
-}
-
-bool MallocHook::AddSampledDeleteHook(SampledDeleteHook hook) {
- return sampled_delete_hooks_.Add(hook);
-}
-
-bool MallocHook::RemoveSampledDeleteHook(SampledDeleteHook hook) {
- return sampled_delete_hooks_.Remove(hook);
-}
-
-bool MallocHook::AddPreMmapHook(PreMmapHook hook) {
- return premmap_hooks_.Add(hook);
-}
-
-bool MallocHook::RemovePreMmapHook(PreMmapHook hook) {
- return premmap_hooks_.Remove(hook);
-}
-
-bool MallocHook::SetMmapReplacement(MmapReplacement hook) {
- // NOTE this is a best effort CHECK. Concurrent sets could succeed since
- // this test is outside of the Add spin lock.
- ABSL_RAW_CHECK(mmap_replacement_.empty(),
- "Only one MMapReplacement is allowed.");
- return mmap_replacement_.Add(hook);
-}
-
-bool MallocHook::RemoveMmapReplacement(MmapReplacement hook) {
- return mmap_replacement_.Remove(hook);
-}
-
-bool MallocHook::AddMmapHook(MmapHook hook) { return mmap_hooks_.Add(hook); }
-
-bool MallocHook::RemoveMmapHook(MmapHook hook) {
- return mmap_hooks_.Remove(hook);
-}
-
-bool MallocHook::SetMunmapReplacement(MunmapReplacement hook) {
- // NOTE this is a best effort CHECK. Concurrent sets could succeed since
- // this test is outside of the Add spin lock.
- ABSL_RAW_CHECK(munmap_replacement_.empty(),
- "Only one MunmapReplacement is allowed.");
- return munmap_replacement_.Add(hook);
-}
-
-bool MallocHook::RemoveMunmapReplacement(MunmapReplacement hook) {
- return munmap_replacement_.Remove(hook);
-}
-
-bool MallocHook::AddMunmapHook(MunmapHook hook) {
- return munmap_hooks_.Add(hook);
-}
-
-bool MallocHook::RemoveMunmapHook(MunmapHook hook) {
- return munmap_hooks_.Remove(hook);
-}
-
-bool MallocHook::AddMremapHook(MremapHook hook) {
- return mremap_hooks_.Add(hook);
-}
-
-bool MallocHook::RemoveMremapHook(MremapHook hook) {
- return mremap_hooks_.Remove(hook);
-}
-
-bool MallocHook::AddPreSbrkHook(PreSbrkHook hook) {
- return presbrk_hooks_.Add(hook);
-}
-
-bool MallocHook::RemovePreSbrkHook(PreSbrkHook hook) {
- return presbrk_hooks_.Remove(hook);
-}
-
-bool MallocHook::AddSbrkHook(SbrkHook hook) { return sbrk_hooks_.Add(hook); }
-
-bool MallocHook::RemoveSbrkHook(SbrkHook hook) {
- return sbrk_hooks_.Remove(hook);
-}
-
-// Note: embedding the function calls inside the traversal of HookList would be
-// very confusing, as it is legal for a hook to remove itself and add other
-// hooks. Doing traversal first, and then calling the hooks ensures we only
-// call the hooks registered at the start.
-#define INVOKE_HOOKS(HookType, hook_list, args) \
- do { \
- HookType hooks[kHookListMaxValues]; \
- int num_hooks = hook_list.Traverse(hooks, kHookListMaxValues); \
- for (int i = 0; i < num_hooks; ++i) { \
- (*hooks[i]) args; \
- } \
- } while (0)
-
-// There should only be one replacement. Return the result of the first
-// one, or false if there is none.
-#define INVOKE_REPLACEMENT(HookType, hook_list, args) \
- do { \
- HookType hooks[kHookListMaxValues]; \
- int num_hooks = hook_list.Traverse(hooks, kHookListMaxValues); \
- return (num_hooks > 0 && (*hooks[0])args); \
- } while (0)
-
-void MallocHook::InvokeNewHookSlow(const void* ptr, size_t size) {
- INVOKE_HOOKS(NewHook, new_hooks_, (ptr, size));
-}
-
-void MallocHook::InvokeDeleteHookSlow(const void* ptr) {
- INVOKE_HOOKS(DeleteHook, delete_hooks_, (ptr));
-}
-
-void MallocHook::InvokeSampledNewHookSlow(const SampledAlloc* sampled_alloc) {
- INVOKE_HOOKS(SampledNewHook, sampled_new_hooks_, (sampled_alloc));
-}
-
-void MallocHook::InvokeSampledDeleteHookSlow(AllocHandle handle) {
- INVOKE_HOOKS(SampledDeleteHook, sampled_delete_hooks_, (handle));
-}
-
-void MallocHook::InvokePreMmapHookSlow(const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset) {
- INVOKE_HOOKS(PreMmapHook, premmap_hooks_, (start, size, protection, flags, fd,
- offset));
-}
-
-void MallocHook::InvokeMmapHookSlow(const void* result,
- const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset) {
- INVOKE_HOOKS(MmapHook, mmap_hooks_, (result, start, size, protection, flags,
- fd, offset));
-}
-
-bool MallocHook::InvokeMmapReplacementSlow(const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset,
- void** result) {
- INVOKE_REPLACEMENT(MmapReplacement, mmap_replacement_,
- (start, size, protection, flags, fd, offset, result));
-}
-
-void MallocHook::InvokeMunmapHookSlow(const void* start, size_t size) {
- INVOKE_HOOKS(MunmapHook, munmap_hooks_, (start, size));
-}
-
-bool MallocHook::InvokeMunmapReplacementSlow(const void* start,
- size_t size,
- int* result) {
- INVOKE_REPLACEMENT(MunmapReplacement, munmap_replacement_,
- (start, size, result));
-}
-
-void MallocHook::InvokeMremapHookSlow(const void* result,
- const void* old_addr,
- size_t old_size,
- size_t new_size,
- int flags,
- const void* new_addr) {
- INVOKE_HOOKS(MremapHook, mremap_hooks_, (result, old_addr, old_size, new_size,
- flags, new_addr));
-}
-
-void MallocHook::InvokePreSbrkHookSlow(ptrdiff_t increment) {
- INVOKE_HOOKS(PreSbrkHook, presbrk_hooks_, (increment));
-}
-
-void MallocHook::InvokeSbrkHookSlow(const void* result, ptrdiff_t increment) {
- INVOKE_HOOKS(SbrkHook, sbrk_hooks_, (result, increment));
-}
-
-#undef INVOKE_HOOKS
-#undef INVOKE_REPLACEMENT
-
-} // namespace base_internal
-} // namespace absl
-
-ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(malloc_hook);
-ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(malloc_hook);
-// actual functions are in this file, malloc_hook.cc, and low_level_alloc.cc
-ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(google_malloc);
-ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(google_malloc);
-ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(blink_malloc);
-ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(blink_malloc);
-
-#define ADDR_IN_ATTRIBUTE_SECTION(addr, name) \
- (reinterpret_cast<uintptr_t>(ABSL_ATTRIBUTE_SECTION_START(name)) <= \
- reinterpret_cast<uintptr_t>(addr) && \
- reinterpret_cast<uintptr_t>(addr) < \
- reinterpret_cast<uintptr_t>(ABSL_ATTRIBUTE_SECTION_STOP(name)))
-
-// Return true iff 'caller' is a return address within a function
-// that calls one of our hooks via MallocHook:Invoke*.
-// A helper for GetCallerStackTrace.
-static inline bool InHookCaller(const void* caller) {
- return ADDR_IN_ATTRIBUTE_SECTION(caller, google_malloc) ||
- ADDR_IN_ATTRIBUTE_SECTION(caller, malloc_hook) ||
- ADDR_IN_ATTRIBUTE_SECTION(caller, blink_malloc);
-
- // We can use one section for everything except tcmalloc_or_debug
- // due to its special linkage mode, which prevents merging of the sections.
-}
-
-#undef ADDR_IN_ATTRIBUTE_SECTION
-
-static absl::once_flag in_hook_caller_once;
-
-static void InitializeInHookCaller() {
- ABSL_INIT_ATTRIBUTE_SECTION_VARS(malloc_hook);
- if (ABSL_ATTRIBUTE_SECTION_START(malloc_hook) ==
- ABSL_ATTRIBUTE_SECTION_STOP(malloc_hook)) {
- ABSL_RAW_LOG(ERROR,
- "malloc_hook section is missing, "
- "thus InHookCaller is broken!");
- }
- ABSL_INIT_ATTRIBUTE_SECTION_VARS(google_malloc);
- if (ABSL_ATTRIBUTE_SECTION_START(google_malloc) ==
- ABSL_ATTRIBUTE_SECTION_STOP(google_malloc)) {
- ABSL_RAW_LOG(ERROR,
- "google_malloc section is missing, "
- "thus InHookCaller is broken!");
- }
- ABSL_INIT_ATTRIBUTE_SECTION_VARS(blink_malloc);
-}
-
-namespace absl {
-namespace base_internal {
-int MallocHook::GetCallerStackTrace(void** result, int max_depth,
- int skip_count,
- GetStackTraceFn get_stack_trace_fn) {
- if (!ABSL_HAVE_ATTRIBUTE_SECTION) {
- // Fall back to get_stack_trace_fn and good old but fragile frame skip
- // counts.
- // Note: this path is inaccurate when a hook is not called directly by an
- // allocation function but is daisy-chained through another hook,
- // search for MallocHook::(Get|Set|Invoke)* to find such cases.
-#ifdef NDEBUG
- return get_stack_trace_fn(result, max_depth, skip_count);
-#else
- return get_stack_trace_fn(result, max_depth, skip_count + 1);
-#endif
- // due to -foptimize-sibling-calls in opt mode
- // there's no need for extra frame skip here then
- }
- absl::call_once(in_hook_caller_once, InitializeInHookCaller);
- // MallocHook caller determination via InHookCaller works, use it:
- static const int kMaxSkip = 32 + 6 + 3;
- // Constant tuned to do just one get_stack_trace_fn call below in practice
- // and not get many frames that we don't actually need:
- // currently max passed max_depth is 32,
- // max passed/needed skip_count is 6
- // and 3 is to account for some hook daisy chaining.
- static const int kStackSize = kMaxSkip + 1;
- void* stack[kStackSize];
- int depth =
- get_stack_trace_fn(stack, kStackSize, 1); // skip this function frame
- if (depth == 0)
- // silently propagate cases when get_stack_trace_fn does not work
- return 0;
- for (int i = depth - 1; i >= 0; --i) { // stack[0] is our immediate caller
- if (InHookCaller(stack[i])) {
- i += 1; // skip hook caller frame
- depth -= i; // correct depth
- if (depth > max_depth) depth = max_depth;
- std::copy(stack + i, stack + i + depth, result);
- if (depth < max_depth && depth + i == kStackSize) {
- // get frames for the missing depth
- depth += get_stack_trace_fn(result + depth, max_depth - depth,
- 1 + kStackSize);
- }
- return depth;
- }
- }
- ABSL_RAW_LOG(WARNING,
- "Hooked allocator frame not found, returning empty trace");
- // If this happens try increasing kMaxSkip
- // or else something must be wrong with InHookCaller,
- // e.g. for every section used in InHookCaller
- // all functions in that section must be inside the same library.
- return 0;
-}
-} // namespace base_internal
-} // namespace absl
-
-// On systems where we know how, we override mmap/munmap/mremap/sbrk
-// to provide support for calling the related hooks (in addition,
-// of course, to doing what these functions normally do).
-
-// The ABSL_MALLOC_HOOK_MMAP_DISABLE macro disables mmap/munmap interceptors.
-// Dynamic tools that intercept mmap/munmap can't be linked together with
-// malloc_hook interceptors. We disable the malloc_hook interceptors for the
-// widely-used dynamic tools, i.e. ThreadSanitizer and MemorySanitizer, but
-// still allow users to disable this in special cases that can't be easily
-// detected during compilation, via -DABSL_MALLOC_HOOK_MMAP_DISABLE or #define
-// ABSL_MALLOC_HOOK_MMAP_DISABLE.
-//
-// TODO(absl-team): Remove MALLOC_HOOK_MMAP_DISABLE in CROSSTOOL for tsan and
-// msan config; Replace MALLOC_HOOK_MMAP_DISABLE with
-// ABSL_MALLOC_HOOK_MMAP_DISABLE for other special cases.
-#if !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) && \
- !defined(ABSL_MALLOC_HOOK_MMAP_DISABLE) && !defined(__ANDROID__) && \
- defined(__linux__)
-#include "absl/base/internal/malloc_hook_mmap_linux.inc"
-
-#elif ABSL_HAVE_MMAP
-
-namespace absl {
-namespace base_internal {
-
-// static
-void* MallocHook::UnhookedMMap(void* start, size_t size, int protection,
- int flags, int fd, off_t offset) {
- void* result;
- if (!MallocHook::InvokeMmapReplacement(
- start, size, protection, flags, fd, offset, &result)) {
- result = mmap(start, size, protection, flags, fd, offset);
- }
- return result;
-}
-
-// static
-int MallocHook::UnhookedMUnmap(void* start, size_t size) {
- int result;
- if (!MallocHook::InvokeMunmapReplacement(start, size, &result)) {
- result = munmap(start, size);
- }
- return result;
-}
-
-} // namespace base_internal
-} // namespace absl
-
-#endif
diff --git a/absl/base/internal/malloc_hook.h b/absl/base/internal/malloc_hook.h
deleted file mode 100644
index 6b006eda..00000000
--- a/absl/base/internal/malloc_hook.h
+++ /dev/null
@@ -1,284 +0,0 @@
-//
-// 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.
-//
-
-// Some of our malloc implementations can invoke the following hooks whenever
-// memory is allocated or deallocated. MallocHook is thread-safe, and things
-// you do before calling AddFooHook(MyHook) are visible to any resulting calls
-// to MyHook. Hooks must be thread-safe. If you write:
-//
-// CHECK(MallocHook::AddNewHook(&MyNewHook));
-//
-// MyNewHook will be invoked in subsequent calls in the current thread, but
-// there are no guarantees on when it might be invoked in other threads.
-//
-// There are a limited number of slots available for each hook type. Add*Hook
-// will return false if there are no slots available. Remove*Hook will return
-// false if the given hook was not already installed.
-//
-// The order in which individual hooks are called in Invoke*Hook is undefined.
-//
-// It is safe for a hook to remove itself within Invoke*Hook and add other
-// hooks. Any hooks added inside a hook invocation (for the same hook type)
-// will not be invoked for the current invocation.
-//
-// One important user of these hooks is the heap profiler.
-//
-// CAVEAT: If you add new MallocHook::Invoke* calls then those calls must be
-// directly in the code of the (de)allocation function that is provided to the
-// user and that function must have an ABSL_ATTRIBUTE_SECTION(malloc_hook)
-// attribute.
-//
-// Note: the Invoke*Hook() functions are defined in malloc_hook-inl.h. If you
-// need to invoke a hook (which you shouldn't unless you're part of tcmalloc),
-// be sure to #include malloc_hook-inl.h in addition to malloc_hook.h.
-//
-// NOTE FOR C USERS: If you want to use malloc_hook functionality from
-// a C program, #include malloc_hook_c.h instead of this file.
-//
-// IWYU pragma: private, include "base/malloc_hook.h"
-
-#ifndef ABSL_BASE_INTERNAL_MALLOC_HOOK_H_
-#define ABSL_BASE_INTERNAL_MALLOC_HOOK_H_
-
-#include <sys/types.h>
-#include <cstddef>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/malloc_hook_c.h"
-#include "absl/base/port.h"
-
-namespace absl {
-namespace base_internal {
-
-// Note: malloc_hook_c.h defines MallocHook_*Hook and
-// MallocHook_{Add,Remove}*Hook. The version of these inside the MallocHook
-// class are defined in terms of the malloc_hook_c version. See malloc_hook_c.h
-// for details of these types/functions.
-
-class MallocHook {
- public:
- // The NewHook is invoked whenever an object is being allocated.
- // Object pointer and size are passed in.
- // It may be passed null pointer if the allocator returned null.
- typedef MallocHook_NewHook NewHook;
- static bool AddNewHook(NewHook hook);
- static bool RemoveNewHook(NewHook hook);
- inline static void InvokeNewHook(const void* ptr, size_t size);
-
- // The DeleteHook is invoked whenever an object is being deallocated.
- // Object pointer is passed in.
- // It may be passed null pointer if the caller is trying to delete null.
- typedef MallocHook_DeleteHook DeleteHook;
- static bool AddDeleteHook(DeleteHook hook);
- static bool RemoveDeleteHook(DeleteHook hook);
- inline static void InvokeDeleteHook(const void* ptr);
-
- // The SampledNewHook is invoked for some subset of object allocations
- // according to the sampling policy of an allocator such as tcmalloc.
- // SampledAlloc has the following fields:
- // * AllocHandle handle: to be set to an effectively unique value (in this
- // process) by allocator.
- // * size_t allocated_size: space actually used by allocator to host the
- // object. Not necessarily equal to the requested size due to alignment
- // and other reasons.
- // * double weight: the expected number of allocations matching this profile
- // that this sample represents.
- // * int stack_depth and const void* stack: invocation stack for
- // the allocation.
- // The allocator invoking the hook should record the handle value and later
- // call InvokeSampledDeleteHook() with that value.
- typedef MallocHook_SampledNewHook SampledNewHook;
- typedef MallocHook_SampledAlloc SampledAlloc;
- static bool AddSampledNewHook(SampledNewHook hook);
- static bool RemoveSampledNewHook(SampledNewHook hook);
- inline static void InvokeSampledNewHook(const SampledAlloc* sampled_alloc);
-
- // The SampledDeleteHook is invoked whenever an object previously chosen
- // by an allocator for sampling is being deallocated.
- // The handle identifying the object --as previously chosen by
- // InvokeSampledNewHook()-- is passed in.
- typedef MallocHook_SampledDeleteHook SampledDeleteHook;
- typedef MallocHook_AllocHandle AllocHandle;
- static bool AddSampledDeleteHook(SampledDeleteHook hook);
- static bool RemoveSampledDeleteHook(SampledDeleteHook hook);
- inline static void InvokeSampledDeleteHook(AllocHandle handle);
-
- // The PreMmapHook is invoked with mmap's or mmap64's arguments just
- // before the mmap/mmap64 call is actually made. Such a hook may be useful
- // in memory limited contexts, to catch allocations that will exceed
- // a memory limit, and take outside actions to increase that limit.
- typedef MallocHook_PreMmapHook PreMmapHook;
- static bool AddPreMmapHook(PreMmapHook hook);
- static bool RemovePreMmapHook(PreMmapHook hook);
- inline static void InvokePreMmapHook(const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset);
-
- // The MmapReplacement is invoked with mmap's arguments and place to put the
- // result into after the PreMmapHook but before the mmap/mmap64 call is
- // actually made.
- // The MmapReplacement should return true if it handled the call, or false
- // if it is still necessary to call mmap/mmap64.
- // This should be used only by experts, and users must be be
- // extremely careful to avoid recursive calls to mmap. The replacement
- // should be async signal safe.
- // Only one MmapReplacement is supported. After setting an MmapReplacement
- // you must call RemoveMmapReplacement before calling SetMmapReplacement
- // again.
- typedef MallocHook_MmapReplacement MmapReplacement;
- static bool SetMmapReplacement(MmapReplacement hook);
- static bool RemoveMmapReplacement(MmapReplacement hook);
- inline static bool InvokeMmapReplacement(const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset,
- void** result);
-
-
- // The MmapHook is invoked with mmap's return value and arguments whenever
- // a region of memory has been just mapped.
- // It may be passed MAP_FAILED if the mmap failed.
- typedef MallocHook_MmapHook MmapHook;
- static bool AddMmapHook(MmapHook hook);
- static bool RemoveMmapHook(MmapHook hook);
- inline static void InvokeMmapHook(const void* result,
- const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset);
-
- // The MunmapReplacement is invoked with munmap's arguments and place to put
- // the result into just before the munmap call is actually made.
- // The MunmapReplacement should return true if it handled the call, or false
- // if it is still necessary to call munmap.
- // This should be used only by experts. The replacement should be
- // async signal safe.
- // Only one MunmapReplacement is supported. After setting an
- // MunmapReplacement you must call RemoveMunmapReplacement before
- // calling SetMunmapReplacement again.
- typedef MallocHook_MunmapReplacement MunmapReplacement;
- static bool SetMunmapReplacement(MunmapReplacement hook);
- static bool RemoveMunmapReplacement(MunmapReplacement hook);
- inline static bool InvokeMunmapReplacement(const void* start,
- size_t size,
- int* result);
-
- // The MunmapHook is invoked with munmap's arguments just before the munmap
- // call is actually made.
- // TODO(maxim): Rename this to PreMunmapHook for consistency with PreMmapHook
- // and PreSbrkHook.
- typedef MallocHook_MunmapHook MunmapHook;
- static bool AddMunmapHook(MunmapHook hook);
- static bool RemoveMunmapHook(MunmapHook hook);
- inline static void InvokeMunmapHook(const void* start, size_t size);
-
- // The MremapHook is invoked with mremap's return value and arguments
- // whenever a region of memory has been just remapped.
- typedef MallocHook_MremapHook MremapHook;
- static bool AddMremapHook(MremapHook hook);
- static bool RemoveMremapHook(MremapHook hook);
- inline static void InvokeMremapHook(const void* result,
- const void* old_addr,
- size_t old_size,
- size_t new_size,
- int flags,
- const void* new_addr);
-
- // The PreSbrkHook is invoked with sbrk's argument just before sbrk is called
- // -- except when the increment is 0. This is because sbrk(0) is often called
- // to get the top of the memory stack, and is not actually a
- // memory-allocation call. It may be useful in memory-limited contexts,
- // to catch allocations that will exceed the limit and take outside
- // actions to increase such a limit.
- typedef MallocHook_PreSbrkHook PreSbrkHook;
- static bool AddPreSbrkHook(PreSbrkHook hook);
- static bool RemovePreSbrkHook(PreSbrkHook hook);
- inline static void InvokePreSbrkHook(ptrdiff_t increment);
-
- // The SbrkHook is invoked with sbrk's result and argument whenever sbrk
- // has just executed -- except when the increment is 0.
- // This is because sbrk(0) is often called to get the top of the memory stack,
- // and is not actually a memory-allocation call.
- typedef MallocHook_SbrkHook SbrkHook;
- static bool AddSbrkHook(SbrkHook hook);
- static bool RemoveSbrkHook(SbrkHook hook);
- inline static void InvokeSbrkHook(const void* result, ptrdiff_t increment);
-
- // Pointer to a absl::GetStackTrace implementation, following the API in
- // base/stacktrace.h.
- using GetStackTraceFn = int (*)(void**, int, int);
-
- // Get the current stack trace. Try to skip all routines up to and
- // including the caller of MallocHook::Invoke*.
- // Use "skip_count" (similarly to absl::GetStackTrace from stacktrace.h)
- // as a hint about how many routines to skip if better information
- // is not available.
- // Stack trace is filled into *result up to the size of max_depth.
- // The actual number of stack frames filled is returned.
- static int GetCallerStackTrace(void** result, int max_depth, int skip_count,
- GetStackTraceFn get_stack_trace_fn);
-
-#if ABSL_HAVE_MMAP
- // Unhooked versions of mmap() and munmap(). These should be used
- // only by experts, since they bypass heapchecking, etc.
- // Note: These do not run hooks, but they still use the MmapReplacement
- // and MunmapReplacement.
- static void* UnhookedMMap(void* start, size_t size, int protection, int flags,
- int fd, off_t offset);
- static int UnhookedMUnmap(void* start, size_t size);
-#endif
-
- private:
- // Slow path versions of Invoke*Hook.
- static void InvokeNewHookSlow(const void* ptr,
- size_t size) ABSL_ATTRIBUTE_COLD;
- static void InvokeDeleteHookSlow(const void* ptr) ABSL_ATTRIBUTE_COLD;
- static void InvokeSampledNewHookSlow(const SampledAlloc* sampled_alloc)
- ABSL_ATTRIBUTE_COLD;
- static void InvokeSampledDeleteHookSlow(AllocHandle handle)
- ABSL_ATTRIBUTE_COLD;
- static void InvokePreMmapHookSlow(const void* start, size_t size,
- int protection, int flags, int fd,
- off_t offset) ABSL_ATTRIBUTE_COLD;
- static void InvokeMmapHookSlow(const void* result, const void* start,
- size_t size, int protection, int flags, int fd,
- off_t offset) ABSL_ATTRIBUTE_COLD;
- static bool InvokeMmapReplacementSlow(const void* start, size_t size,
- int protection, int flags, int fd,
- off_t offset,
- void** result) ABSL_ATTRIBUTE_COLD;
- static void InvokeMunmapHookSlow(const void* ptr,
- size_t size) ABSL_ATTRIBUTE_COLD;
- static bool InvokeMunmapReplacementSlow(const void* ptr, size_t size,
- int* result) ABSL_ATTRIBUTE_COLD;
- static void InvokeMremapHookSlow(const void* result, const void* old_addr,
- size_t old_size, size_t new_size, int flags,
- const void* new_addr) ABSL_ATTRIBUTE_COLD;
- static void InvokePreSbrkHookSlow(ptrdiff_t increment) ABSL_ATTRIBUTE_COLD;
- static void InvokeSbrkHookSlow(const void* result,
- ptrdiff_t increment) ABSL_ATTRIBUTE_COLD;
-};
-
-} // namespace base_internal
-} // namespace absl
-#endif // ABSL_BASE_INTERNAL_MALLOC_HOOK_H_
diff --git a/absl/base/internal/malloc_hook_c.h b/absl/base/internal/malloc_hook_c.h
deleted file mode 100644
index 7255ddc2..00000000
--- a/absl/base/internal/malloc_hook_c.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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_ */
diff --git a/absl/base/internal/malloc_hook_invoke.h b/absl/base/internal/malloc_hook_invoke.h
deleted file mode 100644
index c08220cb..00000000
--- a/absl/base/internal/malloc_hook_invoke.h
+++ /dev/null
@@ -1,198 +0,0 @@
-//
-// 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_
diff --git a/absl/base/internal/malloc_hook_mmap_linux.inc b/absl/base/internal/malloc_hook_mmap_linux.inc
deleted file mode 100644
index b87621a6..00000000
--- a/absl/base/internal/malloc_hook_mmap_linux.inc
+++ /dev/null
@@ -1,168 +0,0 @@
-// 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
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index b92d9056..06bc8009 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -207,7 +207,7 @@ class FixedArray {
// Overload of FixedArray::at() to return a const reference to the ith element
// of the fixed array.
const_reference at(size_type i) const {
- if (i >= size()) {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
}
return data()[i];
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index 0e39855b..26a7d5bc 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -651,7 +651,7 @@ TEST(IntVec, AliasingCopyAssignment) {
IntVec original;
Fill(&original, len);
IntVec dup = original;
- dup = dup;
+ dup = *&dup;
EXPECT_EQ(dup, original);
}
}
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index f6859093..8543200d 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -76,6 +76,24 @@ cc_test(
)
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",
srcs = [
"internal/address_is_readable.cc",
@@ -234,11 +252,11 @@ cc_library(
srcs = ["internal/stack_consumption.cc"],
hdrs = ["internal/stack_consumption.h"],
copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
deps = [
"//absl/base",
"//absl/base:core_headers",
],
- visibility = ["//visibility:private"],
)
cc_test(
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt
index b9aec5fa..8d2ec845 100644
--- a/absl/debugging/CMakeLists.txt
+++ b/absl/debugging/CMakeLists.txt
@@ -25,6 +25,7 @@ list(APPEND DEBUGGING_INTERNAL_HEADERS
"internal/address_is_readable.h"
"internal/demangle.h"
"internal/elf_mem_image.h"
+ "internal/examine_stack.h"
"internal/stacktrace_config.h"
"internal/symbolize.h"
"internal/vdso_support.h"
@@ -49,6 +50,12 @@ list(APPEND SYMBOLIZE_SRC
${DEBUGGING_INTERNAL_HEADERS}
)
+list(APPEND EXAMINE_STACK_SRC
+ "internal/examine_stack.cc"
+ ${DEBUGGING_PUBLIC_HEADERS}
+ ${DEBUGGING_INTERNAL_HEADERS}
+)
+
absl_library(
TARGET
absl_stacktrace
@@ -67,6 +74,17 @@ absl_library(
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
"leak_check.cc"
)
diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc
new file mode 100644
index 00000000..261daae9
--- /dev/null
+++ b/absl/debugging/internal/examine_stack.cc
@@ -0,0 +1,147 @@
+//
+// 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
diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h
new file mode 100644
index 00000000..a16c03b2
--- /dev/null
+++ b/absl/debugging/internal/examine_stack.h
@@ -0,0 +1,38 @@
+//
+// 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_
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index ddc89341..9162bb33 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -168,13 +168,13 @@ class string_view {
string_view( // NOLINT(runtime/explicit)
const std::basic_string<char, std::char_traits<char>, Allocator>&
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
// accepting possibly null strings, use `absl::NullSafeStringView(str)`
// instead (see below).
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.
constexpr string_view(const char* data, size_type len)
@@ -479,7 +479,7 @@ class string_view {
private:
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.
// GCC doesn't have __has_builtin()
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index 3077d240..a26f6c3a 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -1068,6 +1068,17 @@ TEST(HugeStringView, TwoPointTwoGB) {
TEST(NonNegativeLenTest, NonNegativeLen) {
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)
class StringViewStreamTest : public ::testing::Test {
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 354291f2..69f9c81f 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -75,7 +75,6 @@ cc_library(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
- "//absl/base:malloc_extension",
"//absl/base:malloc_internal",
"//absl/debugging:stacktrace",
"//absl/time",
@@ -168,7 +167,6 @@ cc_library(
deps = [
":synchronization",
"//absl/base",
- "//absl/base:malloc_extension",
"//absl/strings",
"//absl/time",
"@com_google_googletest//:gtest",
@@ -184,7 +182,6 @@ cc_test(
":per_thread_sem_test_common",
":synchronization",
"//absl/base",
- "//absl/base:malloc_extension",
"//absl/strings",
"//absl/time",
"@com_google_googletest//:gtest_main",
diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt
index c5c4c626..c8f84faf 100644
--- a/absl/synchronization/CMakeLists.txt
+++ b/absl/synchronization/CMakeLists.txt
@@ -44,7 +44,7 @@ list(APPEND SYNCHRONIZATION_SRC
"notification.cc"
"mutex.cc"
)
-set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl_malloc_extension absl::time)
+set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl::time)
absl_library(
TARGET
diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc
index af872228..caa2baf6 100644
--- a/absl/synchronization/internal/per_thread_sem.cc
+++ b/absl/synchronization/internal/per_thread_sem.cc
@@ -21,7 +21,6 @@
#include <atomic>
#include "absl/base/attributes.h"
-#include "absl/base/internal/malloc_extension.h"
#include "absl/base/internal/thread_identity.h"
#include "absl/synchronization/internal/waiter.h"
@@ -90,12 +89,6 @@ ABSL_ATTRIBUTE_WEAK bool AbslInternalPerThreadSemWait(
if (identity->blocked_count_ptr != nullptr) {
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->wait_start.store(0, std::memory_order_relaxed);
return !timeout;
diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc
index 61296cfc..2b52ea76 100644
--- a/absl/synchronization/internal/per_thread_sem_test.cc
+++ b/absl/synchronization/internal/per_thread_sem_test.cc
@@ -24,7 +24,6 @@
#include "gtest/gtest.h"
#include "absl/base/internal/cycleclock.h"
-#include "absl/base/internal/malloc_extension.h"
#include "absl/base/internal/thread_identity.h"
#include "absl/strings/str_cat.h"
#include "absl/time/clock.h"
@@ -167,79 +166,6 @@ TEST_F(PerThreadSemTest, Timeouts) {
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 synchronization_internal
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc
index db1b54f6..768c5208 100644
--- a/absl/synchronization/internal/waiter.cc
+++ b/absl/synchronization/internal/waiter.cc
@@ -40,8 +40,6 @@
#include <atomic>
#include <cassert>
#include <cstdint>
-
-#include "absl/base/internal/malloc_extension.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/thread_identity.h"
#include "absl/base/optimization.h"
@@ -59,7 +57,6 @@ static void MaybeBecomeIdle() {
const int wait_start = identity->wait_start.load(std::memory_order_relaxed);
if (!is_idle && ticker - wait_start > Waiter::kIdlePeriods) {
identity->is_idle.store(true, std::memory_order_relaxed);
- base_internal::MallocExtension::instance()->MarkThreadIdle();
}
}
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 108fa490..82b4d989 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -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
// bits, and a two's complement representation." So, we can convert to
// 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 int64_t DecodeTwosComp(uint64_t v) { return bit_cast<int64_t>(v); }
+inline uint64_t EncodeTwosComp(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
// equal* because kint64max/min is too large to be represented exactly in a
diff --git a/absl/types/any_exception_safety_test.cc b/absl/types/any_exception_safety_test.cc
index f4ca52eb..82c176b5 100644
--- a/absl/types/any_exception_safety_test.cc
+++ b/absl/types/any_exception_safety_test.cc
@@ -21,15 +21,21 @@
#include "absl/base/internal/exception_safety_testing.h"
using Thrower = absl::ThrowingValue<>;
+using NoThrowMoveThrower =
+ absl::ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>;
using ThrowerList = std::initializer_list<Thrower>;
using ThrowerVec = std::vector<Thrower>;
using ThrowingAlloc = absl::ThrowingAllocator<Thrower>;
using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>;
-namespace absl {
+namespace {
+
+class AnyExceptionSafety : public ::testing::Test {
+ private:
+ absl::ConstructorTracker inspector_;
+};
-testing::AssertionResult AbslCheckInvariants(absl::any* a,
- InternalAbslNamespaceFinder) {
+testing::AssertionResult AnyInvariants(absl::any* a) {
using testing::AssertionFailure;
using testing::AssertionSuccess;
@@ -69,17 +75,10 @@ testing::AssertionResult AbslCheckInvariants(absl::any* a,
return AssertionSuccess();
}
-} // namespace absl
-
-namespace {
-
-class AnyExceptionSafety : public ::testing::Test {
- private:
- absl::ConstructorTracker inspector_;
-};
-
testing::AssertionResult AnyIsEmpty(absl::any* a) {
- if (!a->has_value()) return testing::AssertionSuccess();
+ if (!a->has_value()) {
+ return testing::AssertionSuccess();
+ }
return testing::AssertionFailure()
<< "a should be empty, but instead has value "
<< absl::any_cast<Thrower>(*a).Get();
@@ -100,101 +99,70 @@ TEST_F(AnyExceptionSafety, Ctors) {
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) {
- auto thrower_comp = [](const absl::any& l, const absl::any& r) {
- return absl::any_cast<Thrower>(l) == absl::any_cast<Thrower>(r);
+ auto original =
+ 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);
absl::any any_val(val);
+ NoThrowMoveThrower mv_val(2);
- EXPECT_TRUE(absl::TestExceptionSafety(
- one_factory, [&any_val](absl::any* ap) { *ap = any_val; },
- absl::StrongGuarantee(one_factory, thrower_comp)));
-
- 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)));
+ auto assign_any = [&any_val](absl::any* ap) { *ap = any_val; };
+ auto assign_val = [&val](absl::any* ap) { *ap = val; };
+ 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,
- [&moveable_val](absl::any* ap) { *ap = std::move(moveable_val); },
- absl::StrongGuarantee(one_factory, thrower_comp)));
+ EXPECT_TRUE(any_strong_tester.Test(assign_any));
+ EXPECT_TRUE(any_strong_tester.Test(assign_val));
+ EXPECT_TRUE(any_strong_tester.Test(move));
+ EXPECT_TRUE(any_strong_tester.Test(move_movable));
- EmptyFactory empty_factory;
- auto empty_comp = [](const absl::any& l, const absl::any& r) {
- return !(l.has_value() || r.has_value());
+ auto empty_any_is_strong = [](absl::any* ap) {
+ return testing::AssertionResult{!ap->has_value()};
};
-
- EXPECT_TRUE(absl::TestExceptionSafety(
- empty_factory, [&any_val](absl::any* ap) { *ap = any_val; },
- absl::StrongGuarantee(empty_factory, empty_comp)));
-
- EXPECT_TRUE(absl::TestExceptionSafety(
- empty_factory, [&val](absl::any* ap) { *ap = val; },
- absl::StrongGuarantee(empty_factory, empty_comp)));
-
- EXPECT_TRUE(absl::TestExceptionSafety(
- empty_factory, [&val](absl::any* ap) { *ap = std::move(val); },
- absl::StrongGuarantee(empty_factory, empty_comp)));
+ auto strong_empty_any_tester =
+ absl::MakeExceptionSafetyTester()
+ .WithInitialValue(absl::any{})
+ .WithInvariants(AnyInvariants, empty_any_is_strong);
+
+ EXPECT_TRUE(strong_empty_any_tester.Test(assign_any));
+ EXPECT_TRUE(strong_empty_any_tester.Test(assign_val));
+ EXPECT_TRUE(strong_empty_any_tester.Test(move));
}
// libstdc++ std::any fails this test
#if !defined(ABSL_HAVE_STD_ANY)
TEST_F(AnyExceptionSafety, Emplace) {
- OneFactory one_factory;
-
- EXPECT_TRUE(absl::TestExceptionSafety(
- one_factory, [](absl::any* ap) { ap->emplace<Thrower>(2); }, AnyIsEmpty));
-
- EXPECT_TRUE(absl::TestExceptionSafety(
- one_factory,
- [](absl::any* ap) {
- ap->emplace<absl::ThrowingValue<absl::NoThrow::kMoveCtor |
- absl::NoThrow::kMoveAssign>>(2);
- },
- AnyIsEmpty));
-
- EXPECT_TRUE(absl::TestExceptionSafety(one_factory,
- [](absl::any* ap) {
- std::initializer_list<Thrower> il{
- Thrower(2, absl::no_throw_ctor)};
- ap->emplace<ThrowerVec>(il);
- },
- AnyIsEmpty));
-
- EmptyFactory empty_factory;
- EXPECT_TRUE(absl::TestExceptionSafety(
- 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));
+ auto initial_val =
+ absl::any{absl::in_place_type_t<Thrower>(), 1, absl::no_throw_ctor};
+ auto one_tester = absl::MakeExceptionSafetyTester()
+ .WithInitialValue(initial_val)
+ .WithInvariants(AnyInvariants, AnyIsEmpty);
+
+ auto emp_thrower = [](absl::any* ap) { ap->emplace<Thrower>(2); };
+ auto emp_throwervec = [](absl::any* ap) {
+ std::initializer_list<Thrower> il{Thrower(2, absl::no_throw_ctor)};
+ ap->emplace<ThrowerVec>(il);
+ };
+ auto emp_movethrower = [](absl::any* ap) {
+ ap->emplace<NoThrowMoveThrower>(2);
+ };
+
+ EXPECT_TRUE(one_tester.Test(emp_thrower));
+ EXPECT_TRUE(one_tester.Test(emp_throwervec));
+ EXPECT_TRUE(one_tester.Test(emp_movethrower));
+
+ auto empty_tester = one_tester.WithInitialValue(absl::any{});
+
+ EXPECT_TRUE(empty_tester.Test(emp_thrower));
+ EXPECT_TRUE(empty_tester.Test(emp_throwervec));
}
#endif // ABSL_HAVE_STD_ANY
diff --git a/absl/types/any_test.cc b/absl/types/any_test.cc
index 23aa3a58..115e78df 100644
--- a/absl/types/any_test.cc
+++ b/absl/types/any_test.cc
@@ -553,7 +553,7 @@ TEST(AnyTest, Move) {
absl::any tmp4(4);
absl::any o4(std::move(tmp4)); // move construct
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_TRUE(o4.has_value());
diff --git a/absl/types/span.h b/absl/types/span.h
index d1ef97a2..6ba46685 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -216,7 +216,7 @@ using EnableIfConvertibleToSpanConst =
// // Construct a Span implicitly from a container
// void MyRoutine(absl::Span<const int> a) {
// ...
-// };
+// }
// std::vector v = {1,2,3,4,5};
// MyRoutine(v) // convert to Span<const T>
//
@@ -235,7 +235,7 @@ using EnableIfConvertibleToSpanConst =
//
// void MyRoutine(absl::Span<const int> a) {
// ...
-// };
+// }
//
// std::vector v = {1,2,3,4,5};
// MyRoutine(v);