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