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 43using android::hardware::registerPassthroughServiceImplementation; 44using android::hardware::tests::libhwbinder::V1_0::IScheduleTest; 45using android::sp; 46using std::cerr; 47using std::cout; 48using std::endl; 49using std::fstream; 50using std::left; 51using std::ios; 52using std::get; 53using std::move; 54using std::to_string; 55using std::setprecision; 56using std::setw; 57using std::string; 58using std::vector; 59 60static vector<sp<IScheduleTest> > services; 61 62// default arguments 63static bool dump_raw_data = false; 64static int no_pair = 1; 65static int iterations = 100; 66static int verbose = 0; 67static int is_tracing; 68static bool pass_through = false; 69// the deadline latency that we are interested in 70static uint64_t deadline_us = 2500; 71 72static 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 81static int threadGetPri() { 82 sched_param param; 83 int policy; 84 REQUIRE(!pthread_getschedparam(pthread_self(), &policy, ¶m)); 85 return param.sched_priority; 86} 87 88static 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 106struct ThreadArg { 107 void* result; ///< pointer to PResults 108 int target; ///< the terget service number 109}; 110 111static 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 131static 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 147static 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 160static 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 176static 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 239static 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 255static void waitAll(vector<Pipe>& v) { 256 for (size_t i = 0; i < v.size(); i++) { 257 v[i].wait(); 258 } 259} 260 261static void signalAll(vector<Pipe>& v) { 262 for (size_t i = 0; i < v.size(); i++) { 263 v[i].signal(); 264 } 265} 266 267static 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 284int 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