1 /*
2 * Copyright (C) 2010 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
18 /*
19 * Binder add integers benchmark (Using google-benchmark library)
20 *
21 */
22
23 #include <cerrno>
24 #include <grp.h>
25 #include <iostream>
26 #include <iomanip>
27 #include <libgen.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #include <sys/syscall.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35
36 #include <binder/IPCThreadState.h>
37 #include <binder/ProcessState.h>
38 #include <binder/IServiceManager.h>
39
40 #include <benchmark/benchmark.h>
41
42 #include <utils/Log.h>
43 #include <testUtil.h>
44
45 using namespace android;
46 using namespace std;
47
48 const int unbound = -1; // Indicator for a thread not bound to a specific CPU
49
50 String16 serviceName("test.binderAddInts");
51
52 struct options {
53 int serverCPU;
54 int clientCPU;
55 float iterDelay; // End of iteration delay in seconds
56 } options = { // Set defaults
57 unbound, // Server CPU
58 unbound, // Client CPU
59 0.0, // End of iteration delay
60 };
61
62 class AddIntsService : public BBinder
63 {
64 public:
65 explicit AddIntsService(int cpu = unbound);
~AddIntsService()66 virtual ~AddIntsService() {}
67
68 enum command {
69 ADD_INTS = 0x120,
70 };
71
72 virtual status_t onTransact(uint32_t code,
73 const Parcel& data, Parcel* reply,
74 uint32_t flags = 0);
75
76 private:
77 int cpu_;
78 };
79
80 // File scope function prototypes
81 static bool server(void);
82 static void BM_addInts(benchmark::State& state);
83 static void bindCPU(unsigned int cpu);
84 static ostream &operator<<(ostream &stream, const cpu_set_t& set);
85
server(void)86 static bool server(void)
87 {
88 int rv;
89
90 // Add the service
91 sp<ProcessState> proc(ProcessState::self());
92 sp<IServiceManager> sm = defaultServiceManager();
93 if ((rv = sm->addService(serviceName,
94 new AddIntsService(options.serverCPU))) != 0) {
95 cerr << "addService " << serviceName << " failed, rv: " << rv
96 << " errno: " << errno << endl;
97 return false;
98 }
99
100 // Start threads to handle server work
101 proc->startThreadPool();
102 return true;
103 }
104
BM_addInts(benchmark::State & state)105 static void BM_addInts(benchmark::State& state)
106 {
107 int rv;
108 sp<IServiceManager> sm = defaultServiceManager();
109
110 // If needed bind to client CPU
111 if (options.clientCPU != unbound) { bindCPU(options.clientCPU); }
112
113 // Attach to service
114 sp<IBinder> binder;
115 for (int i = 0; i < 3; i++) {
116 binder = sm->getService(serviceName);
117 if (binder != 0) break;
118 cout << serviceName << " not published, waiting..." << endl;
119 usleep(500000); // 0.5 s
120 }
121
122 if (binder == 0) {
123 cout << serviceName << " failed to publish, aborting" << endl;
124 return;
125 }
126
127 unsigned int iter = 0;
128 // Perform the IPC operations in the benchmark
129 while (state.KeepRunning()) {
130 Parcel send, reply;
131
132 // Create parcel to be sent. Will use the iteration cound
133 // and the iteration count + 3 as the two integer values
134 // to be sent.
135 state.PauseTiming();
136 int val1 = iter;
137 int val2 = iter + 3;
138 int expected = val1 + val2; // Expect to get the sum back
139 send.writeInt32(val1);
140 send.writeInt32(val2);
141 state.ResumeTiming();
142 // Send the parcel, while timing how long it takes for
143 // the answer to return.
144 if ((rv = binder->transact(AddIntsService::ADD_INTS,
145 send, &reply)) != 0) {
146 cerr << "binder->transact failed, rv: " << rv
147 << " errno: " << errno << endl;
148 exit(10);
149 }
150
151 state.PauseTiming();
152 int result = reply.readInt32();
153 if (result != (int) (iter + iter + 3)) {
154 cerr << "Unexpected result for iteration " << iter << endl;
155 cerr << " result: " << result << endl;
156 cerr << "expected: " << expected << endl;
157 }
158
159 if (options.iterDelay > 0.0) { testDelaySpin(options.iterDelay); }
160 state.ResumeTiming();
161 }
162 }
163 BENCHMARK(BM_addInts);
164
165
AddIntsService(int cpu)166 AddIntsService::AddIntsService(int cpu): cpu_(cpu) {
167 if (cpu != unbound) { bindCPU(cpu); }
168 }
169
170 // Server function that handles parcels received from the client
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t)171 status_t AddIntsService::onTransact(uint32_t code, const Parcel &data,
172 Parcel* reply, uint32_t /* flags */) {
173 int val1, val2;
174 status_t rv(0);
175 int cpu;
176
177 // If server bound to a particular CPU, check that
178 // were executing on that CPU.
179 if (cpu_ != unbound) {
180 cpu = sched_getcpu();
181 if (cpu != cpu_) {
182 cerr << "server onTransact on CPU " << cpu << " expected CPU "
183 << cpu_ << endl;
184 exit(20);
185 }
186 }
187
188 // Perform the requested operation
189 switch (code) {
190 case ADD_INTS:
191 val1 = data.readInt32();
192 val2 = data.readInt32();
193 reply->writeInt32(val1 + val2);
194 break;
195
196 default:
197 cerr << "server onTransact unknown code, code: " << code << endl;
198 exit(21);
199 }
200
201 return rv;
202 }
203
bindCPU(unsigned int cpu)204 static void bindCPU(unsigned int cpu)
205 {
206 int rv;
207 cpu_set_t cpuset;
208
209 CPU_ZERO(&cpuset);
210 CPU_SET(cpu, &cpuset);
211 rv = sched_setaffinity(0, sizeof(cpuset), &cpuset);
212
213 if (rv != 0) {
214 cerr << "bindCPU failed, rv: " << rv << " errno: " << errno << endl;
215 perror(NULL);
216 exit(30);
217 }
218 }
219
operator <<(ostream & stream,const cpu_set_t & set)220 static ostream &operator<<(ostream &stream, const cpu_set_t& set)
221 {
222 for (unsigned int n1 = 0; n1 < CPU_SETSIZE; n1++) {
223 if (CPU_ISSET(n1, &set)) {
224 if (n1 != 0) { stream << ' '; }
225 stream << n1;
226 }
227 }
228
229 return stream;
230 }
231
main(int argc,char * argv[])232 int main(int argc, char *argv[])
233 {
234 int rv;
235 ::benchmark::Initialize(&argc, argv);
236 // Determine CPUs available for use.
237 // This testcase limits its self to using CPUs that were
238 // available at the start of the benchmark.
239 cpu_set_t availCPUs;
240 if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) {
241 cerr << "sched_getaffinity failure, rv: " << rv
242 << " errno: " << errno << endl;
243 exit(1);
244 }
245
246 // Parse command line arguments
247 int opt;
248 while ((opt = getopt(argc, argv, "s:c:d:?")) != -1) {
249 char *chptr; // character pointer for command-line parsing
250
251 switch (opt) {
252 case 'c': // client CPU
253 case 's': { // server CPU
254 // Parse the CPU number
255 int cpu = strtoul(optarg, &chptr, 10);
256 if (*chptr != '\0') {
257 cerr << "Invalid cpu specified for -" << (char) opt
258 << " option of: " << optarg << endl;
259 exit(2);
260 }
261
262 // Is the CPU available?
263 if (!CPU_ISSET(cpu, &availCPUs)) {
264 cerr << "CPU " << optarg << " not currently available" << endl;
265 cerr << " Available CPUs: " << availCPUs << endl;
266 exit(3);
267 }
268
269 // Record the choice
270 *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu;
271 break;
272 }
273
274 case 'd': // delay between each iteration
275 options.iterDelay = strtod(optarg, &chptr);
276 if ((*chptr != '\0') || (options.iterDelay < 0.0)) {
277 cerr << "Invalid delay specified of: " << optarg << endl;
278 exit(6);
279 }
280 break;
281
282 case '?':
283 default:
284 cerr << basename(argv[0]) << " [options]" << endl;
285 cerr << " options:" << endl;
286 cerr << " -s cpu - server CPU number" << endl;
287 cerr << " -c cpu - client CPU number" << endl;
288 cerr << " -d time - delay after operation in seconds" << endl;
289 exit(((optopt == 0) || (optopt == '?')) ? 0 : 7);
290 }
291 }
292
293 fflush(stdout);
294 switch (pid_t pid = fork()) {
295 case 0: // Child
296 ::benchmark::RunSpecifiedBenchmarks();
297 return 0;
298
299 default: // Parent
300 if (!server()) { break; }
301
302 // Wait for all children to end
303 do {
304 int stat;
305 rv = wait(&stat);
306 if ((rv == -1) && (errno == ECHILD)) { break; }
307 if (rv == -1) {
308 cerr << "wait failed, rv: " << rv << " errno: "
309 << errno << endl;
310 perror(NULL);
311 exit(8);
312 }
313 } while (1);
314 return 0;
315
316 case -1: // Error
317 exit(9);
318 }
319 }
320