• 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 <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <fstream>
21 #include <iomanip>
22 #include <iostream>
23 #include <string>
24 #include <tuple>
25 #include <vector>
26 
27 #include <pthread.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 
31 using namespace std;
32 using namespace android;
33 using namespace android::hardware;
34 
35 using android::hardware::tests::libhwbinder::V1_0::IScheduleTest;
36 
37 #define ASSERT(cond)                                                \
38   do {                                                              \
39     if (!(cond)) {                                                  \
40       cerr << __func__ << ":" << __LINE__ << " condition:" << #cond \
41            << " failed\n"                                           \
42            << endl;                                                 \
43       exit(EXIT_FAILURE);                                           \
44     }                                                               \
45   } while (0)
46 
47 vector<sp<IScheduleTest> > services;
48 
49 // the ratio that the service is synced on the same cpu beyond
50 // GOOD_SYNC_MIN is considered as good
51 #define GOOD_SYNC_MIN (0.6)
52 
53 #define DUMP_PRICISION 2
54 
55 string trace_path = "/sys/kernel/debug/tracing";
56 
57 // the default value
58 int no_pair = 1;
59 int iterations = 100;
60 int no_inherent = 0;
61 int no_sync = 0;
62 int verbose = 0;
63 int trace;
64 bool pass_through = false;
65 
traceIsOn()66 static bool traceIsOn() {
67   fstream file;
68   file.open(trace_path + "/tracing_on", ios::in);
69   char on;
70   file >> on;
71   file.close();
72   return on == '1';
73 }
74 
traceStop()75 static void traceStop() {
76   ofstream file;
77   file.open(trace_path + "/tracing_on", ios::out | ios::trunc);
78   file << '0' << endl;
79   file.close();
80 }
81 
82 // the deadline latency that we are interested in
83 uint64_t deadline_us = 2500;
84 
threadPri()85 static int threadPri() {
86   struct sched_param param;
87   int policy;
88   ASSERT(!pthread_getschedparam(pthread_self(), &policy, &param));
89   return param.sched_priority;
90 }
91 
threadDump(const char * prefix)92 static void threadDump(const char* prefix) {
93   struct sched_param param;
94   int policy;
95   if (!verbose) return;
96   cout << "--------------------------------------------------" << endl;
97   cout << setw(12) << left << prefix << " pid: " << getpid()
98        << " tid: " << gettid() << " cpu: " << sched_getcpu() << endl;
99   ASSERT(!pthread_getschedparam(pthread_self(), &policy, &param));
100   string s = (policy == SCHED_OTHER)
101                  ? "SCHED_OTHER"
102                  : (policy == SCHED_FIFO)
103                        ? "SCHED_FIFO"
104                        : (policy == SCHED_RR) ? "SCHED_RR" : "???";
105   cout << setw(12) << left << s << param.sched_priority << endl;
106   return;
107 }
108 
109 // This IPC class is widely used in binder/hwbinder tests.
110 // The common usage is the main process to create the Pipe and forks.
111 // Both parent and child hold a object and each wait() on parent
112 // needs a signal() on the child to wake up and vice versa.
113 class Pipe {
114   int m_readFd;
115   int m_writeFd;
Pipe(int readFd,int writeFd)116   Pipe(int readFd, int writeFd) : m_readFd{readFd}, m_writeFd{writeFd} {}
117   Pipe(const Pipe&) = delete;
118   Pipe& operator=(const Pipe&) = delete;
119   Pipe& operator=(const Pipe&&) = delete;
120 
121  public:
Pipe(Pipe && rval)122   Pipe(Pipe&& rval) noexcept {
123     m_readFd = rval.m_readFd;
124     m_writeFd = rval.m_writeFd;
125     rval.m_readFd = 0;
126     rval.m_writeFd = 0;
127   }
~Pipe()128   ~Pipe() {
129     if (m_readFd) close(m_readFd);
130     if (m_writeFd) close(m_writeFd);
131   }
signal()132   void signal() {
133     bool val = true;
134     int error = write(m_writeFd, &val, sizeof(val));
135     ASSERT(error >= 0);
136   };
wait()137   void wait() {
138     bool val = false;
139     int error = read(m_readFd, &val, sizeof(val));
140     ASSERT(error >= 0);
141   }
142   template <typename T>
send(const T & v)143   void send(const T& v) {
144     int error = write(m_writeFd, &v, sizeof(T));
145     ASSERT(error >= 0);
146   }
147   template <typename T>
recv(T & v)148   void recv(T& v) {
149     int error = read(m_readFd, &v, sizeof(T));
150     ASSERT(error >= 0);
151   }
createPipePair()152   static tuple<Pipe, Pipe> createPipePair() {
153     int a[2];
154     int b[2];
155 
156     int error1 = pipe(a);
157     int error2 = pipe(b);
158     ASSERT(error1 >= 0);
159     ASSERT(error2 >= 0);
160 
161     return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
162   }
163 };
164 
165 typedef chrono::time_point<chrono::high_resolution_clock> Tick;
166 
tickNow()167 static inline Tick tickNow() { return chrono::high_resolution_clock::now(); }
168 
tickNano(Tick & sta,Tick & end)169 static inline uint64_t tickNano(Tick& sta, Tick& end) {
170   return uint64_t(
171       chrono::duration_cast<chrono::nanoseconds>(end - sta).count());
172 }
173 
174 struct Results {
175   uint64_t m_best = 0xffffffffffffffffULL;
176   uint64_t m_worst = 0;
177   uint64_t m_transactions = 0;
178   uint64_t m_total_time = 0;
179   uint64_t m_miss = 0;
180   bool tracing;
ResultsResults181   Results(bool _tracing) : tracing(_tracing) {}
miss_deadlineResults182   inline bool miss_deadline(uint64_t nano) { return nano > deadline_us * 1000; }
add_timeResults183   void add_time(uint64_t nano) {
184     m_best = min(nano, m_best);
185     m_worst = max(nano, m_worst);
186     m_transactions += 1;
187     m_total_time += nano;
188     if (miss_deadline(nano)) m_miss++;
189     if (miss_deadline(nano) && tracing) {
190       // There might be multiple process pair running the test concurrently
191       // each may execute following statements and only the first one actually
192       // stop the trace and any traceStop() afterthen has no effect.
193       traceStop();
194       cout << endl;
195       cout << "deadline triggered: halt & stop trace" << endl;
196       cout << "log:" + trace_path + "/trace" << endl;
197       cout << endl;
198       exit(1);
199     }
200   }
dumpResults201   void dump() {
202     double best = (double)m_best / 1.0E6;
203     double worst = (double)m_worst / 1.0E6;
204     double average = (double)m_total_time / m_transactions / 1.0E6;
205     int W = DUMP_PRICISION + 2;
206     cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(W) << left
207          << average << ", \"wst\":" << setw(W) << left << worst
208          << ", \"bst\":" << setw(W) << left << best << ", \"miss\":" << left
209          << m_miss << ", \"meetR\":" << setprecision(DUMP_PRICISION + 3) << left
210          << (1.0 - (double)m_miss / m_transactions) << "}";
211   }
212 };
213 
generateServiceName(int num)214 static string generateServiceName(int num) {
215   string serviceName = "hwbinderService" + to_string(num);
216   return serviceName;
217 }
218 
219 // This private struct is used to pass the argument to threadStart
220 // result: a pointer to Results
221 // target: the terget service number
222 typedef struct {
223   void* result;
224   int target;
225 } thread_priv_t;
226 
threadStart(void * p)227 static void* threadStart(void* p) {
228   thread_priv_t* priv = (thread_priv_t*)p;
229   int target = priv->target;
230   Results* results_fifo = (Results*)priv->result;
231   Tick sta, end;
232 
233   threadDump("fifo-caller");
234   uint32_t call_sta = (threadPri() << 16) | sched_getcpu();
235   sp<IScheduleTest> service = services[target];
236   sta = tickNow();
237   uint32_t ret = service->send(verbose, call_sta);
238   end = tickNow();
239   results_fifo->add_time(tickNano(sta, end));
240 
241   no_inherent += (ret >> 16) & 0xffff;
242   no_sync += ret & 0xffff;
243   return 0;
244 }
245 
246 // create a fifo thread to transact and wait it to finished
threadTransaction(int target,Results * results_fifo)247 static void threadTransaction(int target, Results* results_fifo) {
248   thread_priv_t thread_priv;
249 
250   void* dummy;
251   pthread_t thread;
252   pthread_attr_t attr;
253   struct sched_param param;
254   thread_priv.target = target;
255   thread_priv.result = results_fifo;
256   ASSERT(!pthread_attr_init(&attr));
257   ASSERT(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
258   param.sched_priority = sched_get_priority_max(SCHED_FIFO);
259   ASSERT(!pthread_attr_setschedparam(&attr, &param));
260   ASSERT(!pthread_create(&thread, &attr, threadStart, &thread_priv));
261   ASSERT(!pthread_join(thread, &dummy));
262 }
263 
serviceFx(const string & serviceName,Pipe p)264 static void serviceFx(const string& serviceName, Pipe p) {
265   // Start service.
266   sp<IScheduleTest> server = IScheduleTest::getService(serviceName, true);
267   status_t status = server->registerAsService(serviceName);
268   if (status != ::android::OK) {
269     cout << "Failed to register service " << serviceName.c_str() << endl;
270     exit(EXIT_FAILURE);
271   }
272   // tell main I'm init-ed
273   p.signal();
274   // wait for kill
275   p.wait();
276   exit(EXIT_SUCCESS);
277 }
278 
makeServiceProces(string service_name)279 static Pipe makeServiceProces(string service_name) {
280   auto pipe_pair = Pipe::createPipePair();
281   pid_t pid = fork();
282   if (pid) {
283     // parent
284     return move(get<0>(pipe_pair));
285   } else {
286     threadDump("service");
287     // child
288     serviceFx(service_name, move(get<1>(pipe_pair)));
289     // never get here
290     ASSERT(0);
291     return move(get<0>(pipe_pair));
292   }
293 }
294 
clientFx(int num,int server_count,int iterations,Pipe p)295 static void clientFx(int num, int server_count, int iterations, Pipe p) {
296   Results results_other(false), results_fifo(trace);
297 
298   for (int i = 0; i < server_count; i++) {
299     sp<IScheduleTest> service =
300         IScheduleTest::getService(generateServiceName(i), pass_through);
301     ASSERT(service != nullptr);
302     if (pass_through) {
303       ASSERT(!service->isRemote());
304     } else {
305       ASSERT(service->isRemote());
306     }
307     services.push_back(service);
308   }
309   // tell main I'm init-ed
310   p.signal();
311   // wait for kick-off
312   p.wait();
313 
314   // Client for each pair iterates here
315   // each iterations contains exatcly 2 transactions
316   for (int i = 0; i < iterations; i++) {
317     Tick sta, end;
318     // the target is paired to make it easier to diagnose
319     int target = num;
320 
321     // 1. transaction by fifo thread
322     threadTransaction(target, &results_fifo);
323     threadDump("other-caller");
324 
325     uint32_t call_sta = (threadPri() << 16) | sched_getcpu();
326     sp<IScheduleTest> service = services[target];
327     // 2. transaction by other thread
328     sta = tickNow();
329     uint32_t ret = service->send(verbose, call_sta);
330     end = tickNow();
331     results_other.add_time(tickNano(sta, end));
332     no_inherent += (ret >> 16) & 0xffff;
333     no_sync += ret & 0xffff;
334   }
335   // tell main i'm done
336   p.signal();
337   // wait for kill
338   p.wait();
339   // Client for each pair dump here
340   int no_trans = iterations * 2;
341   double sync_ratio = (1.0 - (double)no_sync / no_trans);
342   cout << "\"P" << num << "\":{\"SYNC\":\""
343        << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
344        << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
345        << "\"R\":" << sync_ratio << "," << endl;
346 
347   cout << "  \"other_ms\":";
348   results_other.dump();
349   cout << "," << endl;
350   cout << "  \"fifo_ms\": ";
351   results_fifo.dump();
352   cout << endl;
353   cout << "}," << endl;
354   exit(no_inherent);
355 }
356 
makeClientProcess(int num,int iterations,int no_pair)357 static Pipe makeClientProcess(int num, int iterations, int no_pair) {
358   auto pipe_pair = Pipe::createPipePair();
359   pid_t pid = fork();
360   if (pid) {
361     // parent
362     return move(get<0>(pipe_pair));
363   } else {
364     // child
365     threadDump("client");
366     clientFx(num, no_pair, iterations, move(get<1>(pipe_pair)));
367     // never get here
368     ASSERT(0);
369     return move(get<0>(pipe_pair));
370   }
371 }
372 
waitAll(vector<Pipe> & v)373 static void waitAll(vector<Pipe>& v) {
374   for (size_t i = 0; i < v.size(); i++) {
375     v[i].wait();
376   }
377 }
378 
signalAll(vector<Pipe> & v)379 static void signalAll(vector<Pipe>& v) {
380   for (size_t i = 0; i < v.size(); i++) {
381     v[i].signal();
382   }
383 }
384 
385 // This test is modified from frameworks/native/libs/binder/tests/sch-dbg.cpp
386 // The difference is sch-dbg tests binder transaction and this one test
387 // HwBinder transaction.
main(int argc,char ** argv)388 int main(int argc, char** argv) {
389   setenv("TREBLE_TESTING_OVERRIDE", "true", true);
390 
391   vector<Pipe> client_pipes;
392   vector<Pipe> service_pipes;
393 
394   for (int i = 1; i < argc; i++) {
395     if (string(argv[i]) == "-passthrough") {
396       pass_through = true;
397     }
398     if (string(argv[i]) == "-i") {
399       iterations = atoi(argv[i + 1]);
400       i++;
401       continue;
402     }
403     if (string(argv[i]) == "-pair") {
404       no_pair = atoi(argv[i + 1]);
405       i++;
406       continue;
407     }
408     if (string(argv[i]) == "-deadline_us") {
409       deadline_us = atoi(argv[i + 1]);
410       i++;
411       continue;
412     }
413     if (string(argv[i]) == "-v") {
414       verbose = 1;
415     }
416     // The -trace argument is used like that:
417     //
418     // First start trace with atrace command as usual
419     // >atrace --async_start sched freq
420     //
421     // then use the -trace arguments like
422     // -trace -deadline_us 2500
423     //
424     // This makes the program to stop trace once it detects a transaction
425     // duration over the deadline. By writing '0' to
426     // /sys/kernel/debug/tracing and halt the process. The tracelog is
427     // then available on /sys/kernel/debug/trace
428     if (string(argv[i]) == "-trace") {
429       trace = 1;
430     }
431   }
432   if (!pass_through) {
433     // Create services.
434     for (int i = 0; i < no_pair; i++) {
435       string serviceName = generateServiceName(i);
436       service_pipes.push_back(makeServiceProces(serviceName));
437     }
438     // Wait until all services are up.
439     waitAll(service_pipes);
440   }
441   if (trace && !traceIsOn()) {
442     cout << "trace is not running" << endl;
443     cout << "check " << trace_path + "/tracing_on" << endl;
444     cout << "use atrace --async_start first" << endl;
445     exit(-1);
446   }
447   threadDump("main");
448   cout << "{" << endl;
449   cout << "\"cfg\":{\"pair\":" << (no_pair) << ",\"iterations\":" << iterations
450        << ",\"deadline_us\":" << deadline_us << "}," << endl;
451 
452   // the main process fork 2 processes for each pairs
453   // 1 server + 1 client
454   // each has a pipe to communicate with
455   for (int i = 0; i < no_pair; i++) {
456     client_pipes.push_back(makeClientProcess(i, iterations, no_pair));
457   }
458   // wait client to init
459   waitAll(client_pipes);
460 
461   // kick off clients
462   signalAll(client_pipes);
463 
464   // wait client to finished
465   waitAll(client_pipes);
466 
467   if (!pass_through) {
468     // Kill all the services.
469     for (int i = 0; i < no_pair; i++) {
470       int status;
471       service_pipes[i].signal();
472       wait(&status);
473       if (status != 0) {
474         cout << "nonzero child status" << status << endl;
475       }
476     }
477   }
478   for (int i = 0; i < no_pair; i++) {
479     int status;
480     client_pipes[i].signal();
481     wait(&status);
482     // the exit status is number of transactions without priority inheritance
483     // detected in the child process
484     no_inherent += status;
485   }
486   cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"")
487        << endl;
488   cout << "}" << endl;
489   return -no_inherent;
490 }
491