diff options
Diffstat (limited to 'libs/binder/tests/binderAllocationLimits.cpp')
-rw-r--r-- | libs/binder/tests/binderAllocationLimits.cpp | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp new file mode 100644 index 0000000000..e1f5ed5381 --- /dev/null +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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 <android-base/logging.h> +#include <binder/Parcel.h> +#include <binder/IServiceManager.h> +#include <gtest/gtest.h> +#include <utils/CallStack.h> + +#include <malloc.h> +#include <functional> +#include <vector> + +struct DestructionAction { + DestructionAction(std::function<void()> f) : mF(std::move(f)) {} + ~DestructionAction() { mF(); }; +private: + std::function<void()> mF; +}; + +// Group of hooks +struct MallocHooks { + decltype(__malloc_hook) malloc_hook; + decltype(__realloc_hook) realloc_hook; + + static MallocHooks save() { + return { + .malloc_hook = __malloc_hook, + .realloc_hook = __realloc_hook, + }; + } + + void overwrite() const { + __malloc_hook = malloc_hook; + __realloc_hook = realloc_hook; + } +}; + +static const MallocHooks orig_malloc_hooks = MallocHooks::save(); + +// When malloc is hit, executes lambda. +namespace LambdaHooks { + using AllocationHook = std::function<void(size_t)>; + static std::vector<AllocationHook> lambdas = {}; + + static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg); + static void* lambda_malloc_hook(size_t bytes, const void* arg); + + static const MallocHooks lambda_malloc_hooks = { + .malloc_hook = lambda_malloc_hook, + .realloc_hook = lambda_realloc_hook, + }; + + static void* lambda_malloc_hook(size_t bytes, const void* arg) { + { + orig_malloc_hooks.overwrite(); + lambdas.at(lambdas.size() - 1)(bytes); + lambda_malloc_hooks.overwrite(); + } + return orig_malloc_hooks.malloc_hook(bytes, arg); + } + + static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) { + { + orig_malloc_hooks.overwrite(); + lambdas.at(lambdas.size() - 1)(bytes); + lambda_malloc_hooks.overwrite(); + } + return orig_malloc_hooks.realloc_hook(ptr, bytes, arg); + } + +} + +// Action to execute when malloc is hit. Supports nesting. Malloc is not +// restricted when the allocation hook is being processed. +__attribute__((warn_unused_result)) +DestructionAction OnMalloc(LambdaHooks::AllocationHook f) { + MallocHooks before = MallocHooks::save(); + LambdaHooks::lambdas.emplace_back(std::move(f)); + LambdaHooks::lambda_malloc_hooks.overwrite(); + return DestructionAction([before]() { + before.overwrite(); + LambdaHooks::lambdas.pop_back(); + }); +} + +// exported symbol, to force compiler not to optimize away pointers we set here +const void* imaginary_use; + +TEST(TestTheTest, OnMalloc) { + size_t mallocs = 0; + { + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + EXPECT_EQ(bytes, 40); + }); + + imaginary_use = new int[10]; + } + EXPECT_EQ(mallocs, 1); +} + + +__attribute__((warn_unused_result)) +DestructionAction ScopeDisallowMalloc() { + return OnMalloc([&](size_t bytes) { + ADD_FAILURE() << "Unexpected allocation: " << bytes; + using android::CallStack; + std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get()) + << std::endl; + }); +} + +using android::IBinder; +using android::Parcel; +using android::String16; +using android::defaultServiceManager; +using android::sp; +using android::IServiceManager; + +static sp<IBinder> GetRemoteBinder() { + // This gets binder representing the service manager + // the current IServiceManager API doesn't expose the binder, and + // I want to avoid adding usages of the AIDL generated interface it + // is using underneath, so to avoid people copying it. + sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager")); + EXPECT_NE(nullptr, binder); + return binder; +} + +TEST(BinderAllocation, ParcelOnStack) { + const auto m = ScopeDisallowMalloc(); + Parcel p; + imaginary_use = p.data(); +} + +TEST(BinderAllocation, GetServiceManager) { + defaultServiceManager(); // first call may alloc + const auto m = ScopeDisallowMalloc(); + defaultServiceManager(); +} + +// note, ping does not include interface descriptor +TEST(BinderAllocation, PingTransaction) { + sp<IBinder> a_binder = GetRemoteBinder(); + const auto m = ScopeDisallowMalloc(); + a_binder->pingBinder(); +} + +TEST(BinderAllocation, SmallTransaction) { + String16 empty_descriptor = String16(""); + sp<IServiceManager> manager = defaultServiceManager(); + + size_t mallocs = 0; + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + // Parcel should allocate a small amount by default + EXPECT_EQ(bytes, 128); + }); + manager->checkService(empty_descriptor); + + EXPECT_EQ(mallocs, 1); +} + +int main(int argc, char** argv) { + if (getenv("LIBC_HOOKS_ENABLE") == nullptr) { + CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/)); + execv(argv[0], argv); + return 1; + } + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} |