1 /*
2  * Copyright (C) 2020 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 
17 #define LOG_TAG "VibratorHalControllerBenchmarks"
18 
19 #include <android/binder_process.h>
20 #include <benchmark/benchmark.h>
21 #include <vibratorservice/VibratorHalController.h>
22 #include <future>
23 
24 using ::aidl::android::hardware::vibrator::CompositeEffect;
25 using ::aidl::android::hardware::vibrator::CompositePrimitive;
26 using ::aidl::android::hardware::vibrator::Effect;
27 using ::aidl::android::hardware::vibrator::EffectStrength;
28 using ::benchmark::Counter;
29 using ::benchmark::Fixture;
30 using ::benchmark::kMicrosecond;
31 using ::benchmark::State;
32 using ::benchmark::internal::Benchmark;
33 
34 using std::chrono::milliseconds;
35 
36 using namespace android;
37 using namespace std::chrono_literals;
38 
39 // Fixed number of iterations for benchmarks that trigger a vibration on the loop.
40 // They require slow cleanup to ensure a stable state on each run and less noisy metrics.
41 static constexpr auto VIBRATION_ITERATIONS = 500;
42 
43 // Timeout to wait for vibration callback completion.
44 static constexpr auto VIBRATION_CALLBACK_TIMEOUT = 100ms;
45 
46 // Max duration the vibrator can be turned on, in milliseconds.
47 static constexpr auto MAX_ON_DURATION_MS = milliseconds(UINT16_MAX);
48 
49 // Helper to wait for the vibrator to become idle between vibrate bench iterations.
50 class HalCallback {
51 public:
HalCallback(std::function<void ()> && waitFn,std::function<void ()> && completeFn)52     HalCallback(std::function<void()>&& waitFn, std::function<void()>&& completeFn)
53           : mWaitFn(std::move(waitFn)), mCompleteFn(std::move(completeFn)) {}
54     ~HalCallback() = default;
55 
completeFn() const56     std::function<void()> completeFn() const { return mCompleteFn; }
57 
waitForComplete() const58     void waitForComplete() const { mWaitFn(); }
59 
60 private:
61     std::function<void()> mWaitFn;
62     std::function<void()> mCompleteFn;
63 };
64 
65 // Helper for vibration callbacks, kept by the Fixture until all pending callbacks are done.
66 class HalCallbacks {
67 public:
next()68     HalCallback next() {
69         std::unique_lock<std::mutex> lock(mMutex);
70         auto id = mCurrentId++;
71         mPendingPromises[id] = std::promise<void>();
72         mPendingFutures[id] = mPendingPromises[id].get_future(); // Can only be called once.
73         return HalCallback([&, id]() { waitForComplete(id); }, [&, id]() { onComplete(id); });
74     }
75 
onComplete(int32_t id)76     void onComplete(int32_t id) {
77         std::unique_lock<std::mutex> lock(mMutex);
78         auto promise = mPendingPromises.find(id);
79         if (promise != mPendingPromises.end()) {
80             promise->second.set_value();
81             mPendingPromises.erase(promise);
82         }
83     }
84 
waitForComplete(int32_t id)85     void waitForComplete(int32_t id) {
86         // Wait until the HAL has finished processing previous vibration before starting a new one,
87         // so the HAL state is consistent on each run and metrics are less noisy. Some of the newest
88         // HAL implementations are waiting on previous vibration cleanup and might be significantly
89         // slower, so make sure we measure vibrations on a clean slate.
90         if (mPendingFutures[id].wait_for(VIBRATION_CALLBACK_TIMEOUT) == std::future_status::ready) {
91             mPendingFutures.erase(id);
92         }
93     }
94 
waitForPending()95     void waitForPending() {
96         // Wait for pending callbacks from the test, possibly skipped with error.
97         for (auto& [id, future] : mPendingFutures) {
98             future.wait_for(VIBRATION_CALLBACK_TIMEOUT);
99         }
100         mPendingFutures.clear();
101         {
102             std::unique_lock<std::mutex> lock(mMutex);
103             mPendingPromises.clear();
104         }
105     }
106 
107 private:
108     std::mutex mMutex;
109     std::map<int32_t, std::promise<void>> mPendingPromises GUARDED_BY(mMutex);
110     std::map<int32_t, std::future<void>> mPendingFutures;
111     int32_t mCurrentId;
112 };
113 
114 class VibratorBench : public Fixture {
115 public:
SetUp(State &)116     void SetUp(State& /*state*/) override {
117         ABinderProcess_setThreadPoolMaxThreadCount(1);
118         ABinderProcess_startThreadPool();
119         mController.init();
120     }
121 
TearDown(State &)122     void TearDown(State& /*state*/) override {
123         turnVibratorOff();
124         disableExternalControl();
125         mCallbacks.waitForPending();
126     }
127 
DefaultConfig(Benchmark * b)128     static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
129 
DefaultArgs(Benchmark *)130     static void DefaultArgs(Benchmark* /*b*/) {
131         // none
132     }
133 
134 protected:
135     vibrator::HalController mController;
136     HalCallbacks mCallbacks;
137 
SlowBenchConfig(Benchmark * b)138     static void SlowBenchConfig(Benchmark* b) { b->Iterations(VIBRATION_ITERATIONS); }
139 
getOtherArg(const State & state,std::size_t index) const140     auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
141 
turnVibratorOff()142     vibrator::HalResult<void> turnVibratorOff() {
143         return mController.doWithRetry<void>([](auto hal) { return hal->off(); }, "off");
144     }
145 
disableExternalControl()146     vibrator::HalResult<void> disableExternalControl() {
147         auto disableExternalControlFn = [](auto hal) { return hal->setExternalControl(false); };
148         return mController.doWithRetry<void>(disableExternalControlFn, "setExternalControl false");
149     }
150 
shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities query,State & state)151     bool shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities query, State& state) {
152         auto result = mController.getInfo().capabilities;
153         if (result.isFailed()) {
154             state.SkipWithError(result.errorMessage());
155             return true;
156         }
157         if (!result.isOk()) {
158             state.SkipWithMessage("capability result is unsupported");
159             return true;
160         }
161         if ((result.value() & query) != query) {
162             state.SkipWithMessage("missing capability");
163             return true;
164         }
165         return false;
166     }
167 
168     template <class R>
shouldSkipWithError(const vibrator::HalFunction<vibrator::HalResult<R>> & halFn,const char * label,State & state)169     bool shouldSkipWithError(const vibrator::HalFunction<vibrator::HalResult<R>>& halFn,
170                              const char* label, State& state) {
171         return shouldSkipWithError(mController.doWithRetry<R>(halFn, label), state);
172     }
173 
174     template <class R>
shouldSkipWithError(const vibrator::HalResult<R> & result,State & state)175     bool shouldSkipWithError(const vibrator::HalResult<R>& result, State& state) {
176         if (result.isFailed()) {
177             state.SkipWithError(result.errorMessage());
178             return true;
179         }
180         return false;
181     }
182 };
183 
184 class SlowVibratorBench : public VibratorBench {
185 public:
DefaultConfig(Benchmark * b)186     static void DefaultConfig(Benchmark* b) {
187         VibratorBench::DefaultConfig(b);
188         SlowBenchConfig(b);
189     }
190 };
191 
192 #define BENCHMARK_WRAPPER(fixt, test, code)                \
193     BENCHMARK_DEFINE_F(fixt, test)                         \
194     /* NOLINTNEXTLINE */                                   \
195     (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \
196             ->Apply(fixt::DefaultConfig)                   \
197             ->Apply(fixt::DefaultArgs)
198 
199 BENCHMARK_WRAPPER(VibratorBench, init, {
200     for (auto _ : state) {
201         // Setup
202         state.PauseTiming();
203         vibrator::HalController controller;
204         state.ResumeTiming();
205 
206         // Test
207         controller.init();
208     }
209 });
210 
211 BENCHMARK_WRAPPER(VibratorBench, initCached, {
212     // First call to cache values.
213     mController.init();
214 
215     for (auto _ : state) {
216         mController.init();
217     }
218 });
219 
220 BENCHMARK_WRAPPER(VibratorBench, ping, {
__anonc7e515f50502(auto hal) 221     auto pingFn = [](auto hal) { return hal->ping(); };
222 
223     for (auto _ : state) {
224         if (shouldSkipWithError<void>(pingFn, "ping", state)) {
225             return;
226         }
227     }
228 });
229 
230 BENCHMARK_WRAPPER(VibratorBench, tryReconnect, {
231     for (auto _ : state) {
232         mController.tryReconnect();
233     }
234 });
235 
236 BENCHMARK_WRAPPER(SlowVibratorBench, on, {
237     auto duration = MAX_ON_DURATION_MS;
238 
239     for (auto _ : state) {
240         // Setup
241         state.PauseTiming();
242         auto cb = mCallbacks.next();
__anonc7e515f50602(auto hal) 243         auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
244         state.ResumeTiming();
245 
246         // Test
247         if (shouldSkipWithError<void>(onFn, "on", state)) {
248             return;
249         }
250 
251         // Cleanup
252         state.PauseTiming();
253         if (shouldSkipWithError(turnVibratorOff(), state)) {
254             return;
255         }
256         cb.waitForComplete();
257         state.ResumeTiming();
258     }
259 });
260 
261 BENCHMARK_WRAPPER(SlowVibratorBench, off, {
262     auto duration = MAX_ON_DURATION_MS;
263 
264     for (auto _ : state) {
265         // Setup
266         state.PauseTiming();
267         auto cb = mCallbacks.next();
__anonc7e515f50702(auto hal) 268         auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
269         if (shouldSkipWithError<void>(onFn, "on", state)) {
270             return;
271         }
__anonc7e515f50802(auto hal) 272         auto offFn = [&](auto hal) { return hal->off(); };
273         state.ResumeTiming();
274 
275         // Test
276         if (shouldSkipWithError<void>(offFn, "off", state)) {
277             return;
278         }
279 
280         // Cleanup
281         state.PauseTiming();
282         cb.waitForComplete();
283         state.ResumeTiming();
284     }
285 });
286 
287 BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
288     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
289         return;
290     }
291 
292     auto duration = MAX_ON_DURATION_MS;
293     auto amplitude = 1.0f;
__anonc7e515f50902(auto hal) 294     auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
295 
__anonc7e515f50a02(auto hal) 296     auto onFn = [&](auto hal) { return hal->on(duration, [&]() {}); };
297     if (shouldSkipWithError<void>(onFn, "on", state)) {
298         return;
299     }
300 
301     for (auto _ : state) {
302         if (shouldSkipWithError<void>(setAmplitudeFn, "setAmplitude", state)) {
303             return;
304         }
305     }
306 });
307 
308 BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
309     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
310         return;
311     }
312 
__anonc7e515f50c02(auto hal) 313     auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
314 
315     for (auto _ : state) {
316         // Test
317         if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
318             return;
319         }
320 
321         // Cleanup
322         state.PauseTiming();
323         if (shouldSkipWithError(disableExternalControl(), state)) {
324             return;
325         }
326         state.ResumeTiming();
327     }
328 });
329 
330 BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitude, {
331     auto externalAmplitudeControl = vibrator::Capabilities::EXTERNAL_CONTROL &
332             vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
333     if (shouldSkipWithMissingCapabilityMessage(externalAmplitudeControl, state)) {
334         return;
335     }
336 
337     auto amplitude = 1.0f;
__anonc7e515f50d02(auto hal) 338     auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
__anonc7e515f50e02(auto hal) 339     auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
340 
341     if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
342         return;
343     }
344 
345     for (auto _ : state) {
346         if (shouldSkipWithError<void>(setAmplitudeFn, "setExternalAmplitude", state)) {
347             return;
348         }
349     }
350 });
351 
352 BENCHMARK_WRAPPER(VibratorBench, getInfo, {
353     for (auto _ : state) {
354         // Setup
355         state.PauseTiming();
356         vibrator::HalController controller;
357         controller.init();
358         state.ResumeTiming();
359 
360         controller.getInfo();
361     }
362 });
363 
364 BENCHMARK_WRAPPER(VibratorBench, getInfoCached, {
365     // First call to cache values.
366     mController.getInfo();
367 
368     for (auto _ : state) {
369         mController.getInfo();
370     }
371 });
372 
373 class VibratorEffectsBench : public VibratorBench {
374 public:
DefaultArgs(Benchmark * b)375     static void DefaultArgs(Benchmark* b) {
376         vibrator::HalController controller;
377         auto effectsResult = controller.getInfo().supportedEffects;
378         if (!effectsResult.isOk()) {
379             return;
380         }
381 
382         std::vector<Effect> supported = effectsResult.value();
383         b->ArgNames({"Effect", "Strength"});
384 
385         if (supported.empty()) {
386             b->Args({static_cast<long>(-1), static_cast<long>(-1)});
387             return;
388         }
389 
390         for (const auto& effect : ndk::enum_range<Effect>()) {
391             if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
392                 continue;
393             }
394             for (const auto& strength : ndk::enum_range<EffectStrength>()) {
395                 b->Args({static_cast<long>(effect), static_cast<long>(strength)});
396             }
397         }
398     }
399 
400 protected:
hasArgs(const State & state) const401     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
402 
getEffect(const State & state) const403     auto getEffect(const State& state) const {
404         return static_cast<Effect>(this->getOtherArg(state, 0));
405     }
406 
getStrength(const State & state) const407     auto getStrength(const State& state) const {
408         return static_cast<EffectStrength>(this->getOtherArg(state, 1));
409     }
410 };
411 
412 class SlowVibratorEffectsBench : public VibratorEffectsBench {
413 public:
DefaultConfig(Benchmark * b)414     static void DefaultConfig(Benchmark* b) {
415         VibratorBench::DefaultConfig(b);
416         SlowBenchConfig(b);
417     }
418 };
419 
420 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
421     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
422         return;
423     }
424     if (!hasArgs(state)) {
425         state.SkipWithMessage("missing args");
426         return;
427     }
428 
429     int32_t id = 1;
430     auto effect = getEffect(state);
431     auto strength = getStrength(state);
__anonc7e515f50f02(auto hal) 432     auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
__anonc7e515f51002(auto hal) 433     auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
434 
435     for (auto _ : state) {
436         // Test
437         if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
438             return;
439         }
440 
441         // Cleanup
442         state.PauseTiming();
443         if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
444             return;
445         }
446         state.ResumeTiming();
447     }
448 });
449 
450 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
451     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
452         return;
453     }
454     if (!hasArgs(state)) {
455         state.SkipWithMessage("missing args");
456         return;
457     }
458 
459     int32_t id = 1;
460     auto effect = getEffect(state);
461     auto strength = getStrength(state);
__anonc7e515f51102(auto hal) 462     auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
__anonc7e515f51202(auto hal) 463     auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
464 
465     for (auto _ : state) {
466         // Setup
467         state.PauseTiming();
468         if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
469             return;
470         }
471         state.ResumeTiming();
472 
473         // Test
474         if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
475             return;
476         }
477     }
478 });
479 
480 BENCHMARK_WRAPPER(SlowVibratorEffectsBench, performEffect, {
481     if (!hasArgs(state)) {
482         state.SkipWithMessage("missing args");
483         return;
484     }
485 
486     auto effect = getEffect(state);
487     auto strength = getStrength(state);
488 
489     for (auto _ : state) {
490         // Setup
491         state.PauseTiming();
492         auto cb = mCallbacks.next();
__anonc7e515f51302(auto hal) 493         auto performFn = [&](auto hal) {
494             return hal->performEffect(effect, strength, cb.completeFn());
495         };
496         state.ResumeTiming();
497 
498         // Test
499         if (shouldSkipWithError<milliseconds>(performFn, "performEffect", state)) {
500             return;
501         }
502 
503         // Cleanup
504         state.PauseTiming();
505         if (shouldSkipWithError(turnVibratorOff(), state)) {
506             return;
507         }
508         cb.waitForComplete();
509         state.ResumeTiming();
510     }
511 });
512 
513 class SlowVibratorPrimitivesBench : public VibratorBench {
514 public:
DefaultConfig(Benchmark * b)515     static void DefaultConfig(Benchmark* b) {
516         VibratorBench::DefaultConfig(b);
517         SlowBenchConfig(b);
518     }
519 
DefaultArgs(Benchmark * b)520     static void DefaultArgs(Benchmark* b) {
521         vibrator::HalController controller;
522         auto primitivesResult = controller.getInfo().supportedPrimitives;
523         if (!primitivesResult.isOk()) {
524             return;
525         }
526 
527         std::vector<CompositePrimitive> supported = primitivesResult.value();
528         b->ArgNames({"Primitive"});
529 
530         if (supported.empty()) {
531             b->Args({static_cast<long>(-1)});
532             return;
533         }
534 
535         for (const auto& primitive : ndk::enum_range<CompositePrimitive>()) {
536             if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
537                 continue;
538             }
539             if (primitive == CompositePrimitive::NOOP) {
540                 continue;
541             }
542             b->Args({static_cast<long>(primitive)});
543         }
544     }
545 
546 protected:
hasArgs(const State & state) const547     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
548 
getPrimitive(const State & state) const549     auto getPrimitive(const State& state) const {
550         return static_cast<CompositePrimitive>(this->getOtherArg(state, 0));
551     }
552 };
553 
554 BENCHMARK_WRAPPER(SlowVibratorPrimitivesBench, performComposedEffect, {
555     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
556         return;
557     }
558     if (!hasArgs(state)) {
559         state.SkipWithMessage("missing args");
560         return;
561     }
562 
563     CompositeEffect effect;
564     effect.primitive = getPrimitive(state);
565     effect.scale = 1.0f;
566     effect.delayMs = static_cast<int32_t>(0);
567 
568     std::vector<CompositeEffect> effects = {effect};
569 
570     for (auto _ : state) {
571         // Setup
572         state.PauseTiming();
573         auto cb = mCallbacks.next();
__anonc7e515f51402(auto hal) 574         auto performFn = [&](auto hal) {
575             return hal->performComposedEffect(effects, cb.completeFn());
576         };
577         state.ResumeTiming();
578 
579         // Test
580         if (shouldSkipWithError<milliseconds>(performFn, "performComposedEffect", state)) {
581             return;
582         }
583 
584         // Cleanup
585         state.PauseTiming();
586         if (shouldSkipWithError(turnVibratorOff(), state)) {
587             return;
588         }
589         cb.waitForComplete();
590         state.ResumeTiming();
591     }
592 });
593 
594 BENCHMARK_MAIN();
595