Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b616049f authored by Devin Moore's avatar Devin Moore
Browse files

Add simple AccessorProvider case to binderAllocationLimits

Help understand where allocations are coming from. Make sure it doesn't
get worse, and provide a way to measure the difference for future
changes.

Test: atest binderAllocationLimits
Bug: 361614498
Change-Id: I2cbf327c0a4fd8c82d02c49ca88aa749084e6ea2
parent 52b4e753
Loading
Loading
Loading
Loading
+118 −4
Original line number Diff line number Diff line
@@ -22,17 +22,33 @@
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
#include <cutils/trace.h>
#include <gtest/gtest-spi.h>
#include <gtest/gtest.h>
#include <utils/CallStack.h>

#include <malloc.h>
#include <atomic>
#include <functional>
#include <numeric>
#include <vector>

using namespace android::binder::impl;

static android::String8 gEmpty(""); // make sure first allocation from optimization runs

struct State {
    State(std::vector<size_t>&& expectedMallocs) : expectedMallocs(std::move(expectedMallocs)) {}
    ~State() {
        size_t num = numMallocs.load();
        if (expectedMallocs.size() != num) {
            ADD_FAILURE() << "Expected " << expectedMallocs.size() << " allocations, but got "
                          << num;
        }
    }
    const std::vector<size_t> expectedMallocs;
    std::atomic<size_t> numMallocs;
};

struct DestructionAction {
    DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
    ~DestructionAction() { mF(); };
@@ -95,8 +111,7 @@ namespace LambdaHooks {

// 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) {
__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();
@@ -106,6 +121,22 @@ DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
    });
}

DestructionAction setExpectedMallocs(std::vector<size_t>&& expected) {
    auto state = std::make_shared<State>(std::move(expected));
    return OnMalloc([state = state](size_t bytes) {
        size_t num = state->numMallocs.fetch_add(1);
        if (num >= state->expectedMallocs.size() || state->expectedMallocs[num] != bytes) {
            ADD_FAILURE() << "Unexpected allocation number " << num << " of size " << bytes
                          << " bytes" << std::endl
                          << android::CallStack::stackToString("UNEXPECTED ALLOCATION",
                                                               android::CallStack::getCurrent(
                                                                       4 /*ignoreDepth*/)
                                                                       .get())
                          << std::endl;
        }
    });
}

// exported symbol, to force compiler not to optimize away pointers we set here
const void* imaginary_use;

@@ -119,16 +150,53 @@ TEST(TestTheTest, OnMalloc) {

        imaginary_use = new int[10];
    }
    delete[] reinterpret_cast<const int*>(imaginary_use);
    EXPECT_EQ(mallocs, 1u);
}

TEST(TestTheTest, OnMallocWithExpectedMallocs) {
    std::vector<size_t> expectedMallocs = {
            4,
            16,
            8,
    };
    {
        const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs));
        imaginary_use = new int32_t[1];
        delete[] reinterpret_cast<const int*>(imaginary_use);
        imaginary_use = new int32_t[4];
        delete[] reinterpret_cast<const int*>(imaginary_use);
        imaginary_use = new int32_t[2];
        delete[] reinterpret_cast<const int*>(imaginary_use);
    }
}

TEST(TestTheTest, OnMallocWithExpectedMallocsWrongSize) {
    std::vector<size_t> expectedMallocs = {
            4,
            16,
            100000,
    };
    EXPECT_NONFATAL_FAILURE(
            {
                const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs));
                imaginary_use = new int32_t[1];
                delete[] reinterpret_cast<const int*>(imaginary_use);
                imaginary_use = new int32_t[4];
                delete[] reinterpret_cast<const int*>(imaginary_use);
                imaginary_use = new int32_t[2];
                delete[] reinterpret_cast<const int*>(imaginary_use);
            },
            "Unexpected allocation number 2 of size 8 bytes");
}

__attribute__((warn_unused_result))
DestructionAction ScopeDisallowMalloc() {
    return OnMalloc([&](size_t bytes) {
        ADD_FAILURE() << "Unexpected allocation: " << bytes;
        FAIL() << "Unexpected allocation: " << bytes;
        using android::CallStack;
        std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
        std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION",
                                              CallStack::getCurrent(4 /*ignoreDepth*/).get())
                  << std::endl;
    });
}
@@ -224,6 +292,51 @@ TEST(BinderAllocation, SmallTransaction) {
    EXPECT_EQ(mallocs, 1u);
}

TEST(BinderAccessorAllocation, AddAccessorCheckService) {
    // Need to call defaultServiceManager() before checking malloc because it
    // will allocate an instance in the call_once
    const auto sm = defaultServiceManager();
    const std::string kInstanceName1 = "foo.bar.IFoo/default";
    const std::string kInstanceName2 = "foo.bar.IFoo2/default";
    const String16 kInstanceName16(kInstanceName1.c_str());
    std::vector<size_t> expectedMallocs = {
            // addAccessorProvider
            112, // new AccessorProvider
            16,  // new AccessorProviderEntry
            // checkService
            45,  // String8 from String16 in CppShim::checkService
            128, // writeInterfaceToken
            16,  // getInjectedAccessor, new AccessorProviderEntry
            66,  // getInjectedAccessor, String16
            45,  // String8 from String16 in AccessorProvider::provide
    };
    std::set<std::string> supportedInstances = {kInstanceName1, kInstanceName2};
    auto onMalloc = setExpectedMallocs(std::move(expectedMallocs));

    auto receipt =
            android::addAccessorProvider(std::move(supportedInstances),
                                         [&](const String16&) -> sp<IBinder> { return nullptr; });
    EXPECT_FALSE(receipt.expired());

    sp<IBinder> binder = sm->checkService(kInstanceName16);

    status_t status = android::removeAccessorProvider(receipt);
}

TEST(BinderAccessorAllocation, AddAccessorEmpty) {
    std::vector<size_t> expectedMallocs = {
            48, // From ALOGE with empty set of instances
    };
    std::set<std::string> supportedInstances = {};
    auto onMalloc = setExpectedMallocs(std::move(expectedMallocs));

    auto receipt =
            android::addAccessorProvider(std::move(supportedInstances),
                                         [&](const String16&) -> sp<IBinder> { return nullptr; });

    EXPECT_TRUE(receipt.expired());
}

TEST(RpcBinderAllocation, SetupRpcServer) {
    std::string tmp = getenv("TMPDIR") ?: "/tmp";
    std::string addr = tmp + "/binderRpcBenchmark";
@@ -255,6 +368,7 @@ TEST(RpcBinderAllocation, SetupRpcServer) {
}

int main(int argc, char** argv) {
    LOG(INFO) << "Priming static log variables for binderAllocationLimits.";
    if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
        CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
        execv(argv[0], argv);