1 /*
2 * Copyright (C) 2016 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 #include <android/hardware/tests/libhwbinder/1.0/IScheduleTest.h>
17 #include <hidl/LegacySupport.h>
18 #include <pthread.h>
19 #include <sys/wait.h>
20 #include <fstream>
21 #include <iomanip>
22 #include <iostream>
23 #include <string>
24 #include "PerfTest.h"
25
26 #ifdef ASSERT
27 #undef ASSERT
28 #endif
29 #define ASSERT(cond) \
30 do { \
31 if (!(cond)) { \
32 cerr << __func__ << ":" << __LINE__ << " condition:" << #cond << " failed\n" << endl; \
33 exit(EXIT_FAILURE); \
34 } \
35 } while (0)
36
37 #define REQUIRE(stat) \
38 do { \
39 int cond = (stat); \
40 ASSERT(cond); \
41 } while (0)
42
43 using android::hardware::registerPassthroughServiceImplementation;
44 using android::hardware::tests::libhwbinder::V1_0::IScheduleTest;
45 using android::sp;
46 using std::cerr;
47 using std::cout;
48 using std::endl;
49 using std::fstream;
50 using std::left;
51 using std::ios;
52 using std::get;
53 using std::move;
54 using std::to_string;
55 using std::setprecision;
56 using std::setw;
57 using std::string;
58 using std::vector;
59
60 static vector<sp<IScheduleTest> > services;
61
62 // default arguments
63 static bool dump_raw_data = false;
64 static int no_pair = 1;
65 static int iterations = 100;
66 static int verbose = 0;
67 static int is_tracing;
68 static bool pass_through = false;
69 // the deadline latency that we are interested in
70 static uint64_t deadline_us = 2500;
71
traceIsOn()72 static bool traceIsOn() {
73 fstream file;
74 file.open(TRACE_PATH "/tracing_on", ios::in);
75 char on;
76 file >> on;
77 file.close();
78 return on == '1';
79 }
80
threadGetPri()81 static int threadGetPri() {
82 sched_param param;
83 int policy;
84 REQUIRE(!pthread_getschedparam(pthread_self(), &policy, ¶m));
85 return param.sched_priority;
86 }
87
threadDumpPri(const char * prefix)88 static void threadDumpPri(const char* prefix) {
89 sched_param param;
90 int policy;
91 if (!verbose) {
92 return;
93 }
94 cout << "--------------------------------------------------" << endl;
95 cout << setw(12) << left << prefix << " pid: " << getpid() << " tid: " << gettid()
96 << " cpu: " << sched_getcpu() << endl;
97 REQUIRE(!pthread_getschedparam(pthread_self(), &policy, ¶m));
98 string s =
99 (policy == SCHED_OTHER)
100 ? "SCHED_OTHER"
101 : (policy == SCHED_FIFO) ? "SCHED_FIFO" : (policy == SCHED_RR) ? "SCHED_RR" : "???";
102 cout << setw(12) << left << s << param.sched_priority << endl;
103 return;
104 }
105
106 struct ThreadArg {
107 void* result; ///< pointer to PResults
108 int target; ///< the terget service number
109 };
110
threadStart(void * p)111 static void* threadStart(void* p) {
112 ThreadArg* priv = (ThreadArg*)p;
113 int target = priv->target;
114 PResults* presults = (PResults*)priv->result;
115 Tick sta, end;
116
117 threadDumpPri("fifo-caller");
118 uint32_t call_sta = (threadGetPri() << 16) | sched_getcpu();
119 sp<IScheduleTest> service = services[target];
120 TICK_NOW(sta);
121 uint32_t ret = service->send(verbose, call_sta);
122 TICK_NOW(end);
123 presults->fifo.addTime(tickDiffNS(sta, end));
124
125 presults->nNotInherent += (ret >> 16) & 0xffff;
126 presults->nNotSync += ret & 0xffff;
127 return 0;
128 }
129
130 // create a fifo thread to transact and wait it to finished
threadTransaction(int target,PResults * presults)131 static void threadTransaction(int target, PResults* presults) {
132 ThreadArg thread_arg;
133 void* dummy;
134 pthread_t thread;
135 pthread_attr_t attr;
136 sched_param param;
137 thread_arg.target = target;
138 thread_arg.result = presults;
139 REQUIRE(!pthread_attr_init(&attr));
140 REQUIRE(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
141 param.sched_priority = sched_get_priority_max(SCHED_FIFO);
142 REQUIRE(!pthread_attr_setschedparam(&attr, ¶m));
143 REQUIRE(!pthread_create(&thread, &attr, threadStart, &thread_arg));
144 REQUIRE(!pthread_join(thread, &dummy));
145 }
146
serviceFx(const string & serviceName,Pipe p)147 static void serviceFx(const string& serviceName, Pipe p) {
148 // Start service.
149 if (registerPassthroughServiceImplementation<IScheduleTest>(serviceName) != ::android::OK) {
150 cerr << "Failed to register service " << serviceName.c_str() << endl;
151 exit(EXIT_FAILURE);
152 }
153 // tell main I'm init-ed
154 p.signal();
155 // wait for kill
156 p.wait();
157 exit(0);
158 }
159
makeServiceProces(string service_name)160 static Pipe makeServiceProces(string service_name) {
161 auto pipe_pair = Pipe::createPipePair();
162 pid_t pid = fork();
163 if (pid) {
164 // parent
165 return move(get<0>(pipe_pair));
166 } else {
167 threadDumpPri("service");
168 // child
169 serviceFx(service_name, move(get<1>(pipe_pair)));
170 // never get here
171 ASSERT(0);
172 return move(get<0>(pipe_pair));
173 }
174 }
175
clientFx(int num,int server_count,int iterations,Pipe p)176 static void clientFx(int num, int server_count, int iterations, Pipe p) {
177 PResults presults;
178
179 presults.fifo.setTracingMode(is_tracing, deadline_us);
180 if (dump_raw_data) {
181 presults.fifo.setupRawData();
182 }
183
184 for (int i = 0; i < server_count; i++) {
185 sp<IScheduleTest> service =
186 IScheduleTest::getService("hwbinderService" + to_string(i), pass_through);
187 ASSERT(service != nullptr);
188 if (pass_through) {
189 ASSERT(!service->isRemote());
190 } else {
191 ASSERT(service->isRemote());
192 }
193 services.push_back(service);
194 }
195 // tell main I'm init-ed
196 p.signal();
197 // wait for kick-off
198 p.wait();
199
200 // Client for each pair iterates here
201 // each iterations contains exactly 2 transactions
202 for (int i = 0; i < iterations; i++) {
203 Tick sta, end;
204 // the target is paired to make it easier to diagnose
205 int target = num;
206
207 // 1. transaction by fifo thread
208 threadTransaction(target, &presults);
209 threadDumpPri("other-caller");
210
211 uint32_t call_sta = (threadGetPri() << 16) | sched_getcpu();
212 sp<IScheduleTest> service = services[target];
213 // 2. transaction by other thread
214 TICK_NOW(sta);
215 uint32_t ret = service->send(verbose, call_sta);
216 TICK_NOW(end);
217 presults.other.addTime(tickDiffNS(sta, end));
218 presults.nNotInherent += (ret >> 16) & 0xffff;
219 presults.nNotSync += ret & 0xffff;
220 }
221 // tell main i'm done
222 p.signal();
223
224 // wait to send result
225 p.wait();
226 if (dump_raw_data) {
227 cout << "\"fifo_" + to_string(num) + "_data\": ";
228 presults.flushRawData();
229 }
230 cout.flush();
231 int sent = p.send(presults);
232 ASSERT(sent >= 0);
233
234 // wait for kill
235 p.wait();
236 exit(0);
237 }
238
makeClientProcess(int num,int iterations,int no_pair)239 static Pipe makeClientProcess(int num, int iterations, int no_pair) {
240 auto pipe_pair = Pipe::createPipePair();
241 pid_t pid = fork();
242 if (pid) {
243 // parent
244 return move(get<0>(pipe_pair));
245 } else {
246 // child
247 threadDumpPri("client");
248 clientFx(num, no_pair, iterations, move(get<1>(pipe_pair)));
249 // never get here
250 ASSERT(0);
251 return move(get<0>(pipe_pair));
252 }
253 }
254
waitAll(vector<Pipe> & v)255 static void waitAll(vector<Pipe>& v) {
256 for (size_t i = 0; i < v.size(); i++) {
257 v[i].wait();
258 }
259 }
260
signalAll(vector<Pipe> & v)261 static void signalAll(vector<Pipe>& v) {
262 for (size_t i = 0; i < v.size(); i++) {
263 v[i].signal();
264 }
265 }
266
help()267 static void help() {
268 cout << "usage:" << endl;
269 cout << "-i 1 # number of iterations" << endl;
270 cout << "-pair 4 # number of process pairs" << endl;
271 cout << "-deadline_us 2500 # deadline in us" << endl;
272 cout << "-v # debug" << endl;
273 cout << "-raw_data # dump raw data" << endl;
274 cout << "-trace # halt the trace on a dealine hit" << endl;
275 exit(0);
276 }
277
278 // Test:
279 //
280 // libhwbinder_latency -i 1 -v
281 // libhwbinder_latency -i 10000 -pair 4
282 // atrace --async_start -c sched idle workq binder_driver freq && \
283 // libhwbinder_latency -i 10000 -pair 4 -trace
main(int argc,char ** argv)284 int main(int argc, char** argv) {
285 setenv("TREBLE_TESTING_OVERRIDE", "true", true);
286
287 vector<Pipe> client_pipes;
288 vector<Pipe> service_pipes;
289
290 for (int i = 1; i < argc; i++) {
291 if (string(argv[i]) == "-h") {
292 help();
293 }
294 if (string(argv[i]) == "-m") {
295 if (!strcmp(argv[i + 1], "PASSTHROUGH")) {
296 pass_through = true;
297 }
298 i++;
299 continue;
300 }
301 if (string(argv[i]) == "-i") {
302 iterations = atoi(argv[i + 1]);
303 i++;
304 continue;
305 }
306 if (string(argv[i]) == "-pair" || string(argv[i]) == "-w") {
307 no_pair = atoi(argv[i + 1]);
308 i++;
309 continue;
310 }
311 if (string(argv[i]) == "-deadline_us") {
312 deadline_us = atoi(argv[i + 1]);
313 i++;
314 continue;
315 }
316 if (string(argv[i]) == "-v") {
317 verbose = 1;
318 }
319 if (string(argv[i]) == "-raw_data") {
320 dump_raw_data = true;
321 }
322 // The -trace argument is used like that:
323 //
324 // First start trace with atrace command as usual
325 // >atrace --async_start sched freq
326 //
327 // then use the -trace arguments like
328 // -trace -deadline_us 2500
329 //
330 // This makes the program to stop trace once it detects a transaction
331 // duration over the deadline. By writing '0' to
332 // /sys/kernel/debug/tracing and halt the process. The tracelog is
333 // then available on /sys/kernel/debug/trace
334 if (string(argv[i]) == "-trace") {
335 is_tracing = 1;
336 }
337 }
338 if (!pass_through) {
339 // Create services.
340 for (int i = 0; i < no_pair; i++) {
341 service_pipes.push_back(makeServiceProces("hwbinderService" + to_string(i)));
342 }
343 // Wait until all services are up.
344 waitAll(service_pipes);
345 }
346 if (is_tracing && !traceIsOn()) {
347 cerr << "trace is not running" << endl;
348 cerr << "check " << TRACE_PATH "/tracing_on" << endl;
349 cerr << "use atrace --async_start first" << endl;
350 exit(EXIT_FAILURE);
351 }
352 threadDumpPri("main");
353 cout << "{" << endl;
354 cout << "\"cfg\":{\"pair\":" << (no_pair) << ",\"iterations\":" << iterations
355 << ",\"deadline_us\":" << deadline_us << ",\"passthrough\":" << pass_through << "},"
356 << endl;
357
358 // the main process fork 2 processes for each pairs
359 // 1 server + 1 client
360 // each has a pipe to communicate with
361 for (int i = 0; i < no_pair; i++) {
362 client_pipes.push_back(makeClientProcess(i, iterations, no_pair));
363 }
364 // wait client to init
365 waitAll(client_pipes);
366
367 // kick off clients
368 signalAll(client_pipes);
369
370 // wait client to finished
371 waitAll(client_pipes);
372
373 // collect all results
374 PResults total, presults[no_pair];
375 for (int i = 0; i < no_pair; i++) {
376 client_pipes[i].signal();
377 int recvd = client_pipes[i].recv(presults[i]);
378 ASSERT(recvd >= 0);
379 total = PResults::combine(total, presults[i]);
380 }
381 cout << "\"ALL\":";
382 total.dump();
383 for (int i = 0; i < no_pair; i++) {
384 cout << "\"P" << i << "\":";
385 presults[i].dump();
386 }
387
388 if (!pass_through) {
389 signalAll(service_pipes);
390 }
391 int nNotInherent = 0;
392 for (int i = 0; i < no_pair; i++) {
393 nNotInherent += presults[i].nNotInherent;
394 }
395 cout << "\"inheritance\": " << (nNotInherent == 0 ? "\"PASS\"" : "\"FAIL\"") << endl;
396 cout << "}" << endl;
397 // kill all
398 signalAll(client_pipes);
399 return -nNotInherent;
400 }
401