blob: a2ab8ab3027a525ad85392d81c4d03e0d4f49fa3 [file] [log] [blame]
Steven Moreland042ae822020-05-27 17:45:17 +00001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <android-base/logging.h>
Devin Moore2a49be62022-05-26 22:04:21 +000018#include <binder/Binder.h>
Steven Moreland042ae822020-05-27 17:45:17 +000019#include <binder/IServiceManager.h>
Devin Moore2a49be62022-05-26 22:04:21 +000020#include <binder/Parcel.h>
21#include <binder/RpcServer.h>
22#include <binder/RpcSession.h>
Steven Moreland042ae822020-05-27 17:45:17 +000023#include <gtest/gtest.h>
24#include <utils/CallStack.h>
25
26#include <malloc.h>
27#include <functional>
28#include <vector>
29
Devin Moorea39f3db2022-08-15 23:28:55 +000030static android::String8 gEmpty(""); // make sure first allocation from optimization runs
31
Steven Moreland042ae822020-05-27 17:45:17 +000032struct DestructionAction {
33 DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
34 ~DestructionAction() { mF(); };
35private:
36 std::function<void()> mF;
37};
38
39// Group of hooks
40struct MallocHooks {
41 decltype(__malloc_hook) malloc_hook;
42 decltype(__realloc_hook) realloc_hook;
43
44 static MallocHooks save() {
45 return {
46 .malloc_hook = __malloc_hook,
47 .realloc_hook = __realloc_hook,
48 };
49 }
50
51 void overwrite() const {
52 __malloc_hook = malloc_hook;
53 __realloc_hook = realloc_hook;
54 }
55};
56
57static const MallocHooks orig_malloc_hooks = MallocHooks::save();
58
59// When malloc is hit, executes lambda.
60namespace LambdaHooks {
61 using AllocationHook = std::function<void(size_t)>;
62 static std::vector<AllocationHook> lambdas = {};
63
64 static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg);
65 static void* lambda_malloc_hook(size_t bytes, const void* arg);
66
67 static const MallocHooks lambda_malloc_hooks = {
68 .malloc_hook = lambda_malloc_hook,
69 .realloc_hook = lambda_realloc_hook,
70 };
71
72 static void* lambda_malloc_hook(size_t bytes, const void* arg) {
73 {
74 orig_malloc_hooks.overwrite();
75 lambdas.at(lambdas.size() - 1)(bytes);
76 lambda_malloc_hooks.overwrite();
77 }
78 return orig_malloc_hooks.malloc_hook(bytes, arg);
79 }
80
81 static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) {
82 {
83 orig_malloc_hooks.overwrite();
84 lambdas.at(lambdas.size() - 1)(bytes);
85 lambda_malloc_hooks.overwrite();
86 }
87 return orig_malloc_hooks.realloc_hook(ptr, bytes, arg);
88 }
89
90}
91
92// Action to execute when malloc is hit. Supports nesting. Malloc is not
93// restricted when the allocation hook is being processed.
94__attribute__((warn_unused_result))
95DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
96 MallocHooks before = MallocHooks::save();
97 LambdaHooks::lambdas.emplace_back(std::move(f));
98 LambdaHooks::lambda_malloc_hooks.overwrite();
99 return DestructionAction([before]() {
100 before.overwrite();
101 LambdaHooks::lambdas.pop_back();
102 });
103}
104
105// exported symbol, to force compiler not to optimize away pointers we set here
106const void* imaginary_use;
107
108TEST(TestTheTest, OnMalloc) {
109 size_t mallocs = 0;
110 {
111 const auto on_malloc = OnMalloc([&](size_t bytes) {
112 mallocs++;
113 EXPECT_EQ(bytes, 40);
114 });
115
116 imaginary_use = new int[10];
117 }
118 EXPECT_EQ(mallocs, 1);
119}
120
121
122__attribute__((warn_unused_result))
123DestructionAction ScopeDisallowMalloc() {
124 return OnMalloc([&](size_t bytes) {
125 ADD_FAILURE() << "Unexpected allocation: " << bytes;
126 using android::CallStack;
127 std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
128 << std::endl;
129 });
130}
131
Devin Moore2a49be62022-05-26 22:04:21 +0000132using android::BBinder;
Steven Moreland042ae822020-05-27 17:45:17 +0000133using android::defaultServiceManager;
Devin Moore2a49be62022-05-26 22:04:21 +0000134using android::IBinder;
Steven Moreland042ae822020-05-27 17:45:17 +0000135using android::IServiceManager;
Devin Moore2a49be62022-05-26 22:04:21 +0000136using android::OK;
137using android::Parcel;
138using android::RpcServer;
139using android::RpcSession;
140using android::sp;
141using android::status_t;
142using android::statusToString;
143using android::String16;
Steven Moreland042ae822020-05-27 17:45:17 +0000144
145static sp<IBinder> GetRemoteBinder() {
146 // This gets binder representing the service manager
147 // the current IServiceManager API doesn't expose the binder, and
148 // I want to avoid adding usages of the AIDL generated interface it
149 // is using underneath, so to avoid people copying it.
150 sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager"));
151 EXPECT_NE(nullptr, binder);
152 return binder;
153}
154
155TEST(BinderAllocation, ParcelOnStack) {
156 const auto m = ScopeDisallowMalloc();
157 Parcel p;
158 imaginary_use = p.data();
159}
160
161TEST(BinderAllocation, GetServiceManager) {
162 defaultServiceManager(); // first call may alloc
163 const auto m = ScopeDisallowMalloc();
164 defaultServiceManager();
165}
166
167// note, ping does not include interface descriptor
168TEST(BinderAllocation, PingTransaction) {
169 sp<IBinder> a_binder = GetRemoteBinder();
170 const auto m = ScopeDisallowMalloc();
171 a_binder->pingBinder();
172}
173
174TEST(BinderAllocation, SmallTransaction) {
175 String16 empty_descriptor = String16("");
176 sp<IServiceManager> manager = defaultServiceManager();
177
178 size_t mallocs = 0;
179 const auto on_malloc = OnMalloc([&](size_t bytes) {
180 mallocs++;
181 // Parcel should allocate a small amount by default
182 EXPECT_EQ(bytes, 128);
183 });
184 manager->checkService(empty_descriptor);
185
186 EXPECT_EQ(mallocs, 1);
187}
188
Devin Moore2a49be62022-05-26 22:04:21 +0000189TEST(RpcBinderAllocation, SetupRpcServer) {
190 std::string tmp = getenv("TMPDIR") ?: "/tmp";
191 std::string addr = tmp + "/binderRpcBenchmark";
192 (void)unlink(addr.c_str());
193 auto server = RpcServer::make();
194 server->setRootObject(sp<BBinder>::make());
195
196 CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
197
198 std::thread([server]() { server->join(); }).detach();
199
200 status_t status;
201 auto session = RpcSession::make();
202 status = session->setupUnixDomainClient(addr.c_str());
203 CHECK_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str();
204
205 auto remoteBinder = session->getRootObject();
206
207 size_t mallocs = 0, totalBytes = 0;
Frederick Mayle68aa3bc2022-06-07 15:51:31 +0000208 {
209 const auto on_malloc = OnMalloc([&](size_t bytes) {
210 mallocs++;
211 totalBytes += bytes;
212 });
213 CHECK_EQ(OK, remoteBinder->pingBinder());
214 }
Frederick Mayleb86cda42022-06-09 23:17:45 +0000215 EXPECT_EQ(mallocs, 1);
216 EXPECT_EQ(totalBytes, 40);
Devin Moore2a49be62022-05-26 22:04:21 +0000217}
218
Steven Moreland042ae822020-05-27 17:45:17 +0000219int main(int argc, char** argv) {
220 if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
221 CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
222 execv(argv[0], argv);
223 return 1;
224 }
225 ::testing::InitGoogleTest(&argc, argv);
226 return RUN_ALL_TESTS();
227}