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, &param));
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, &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
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, &param));
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