• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &param));
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, &param));
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, &param));
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