blob: b690d8d98e33aecb0e2023159c12cc499c403bc8 [file] [log] [blame]
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <ftl/enum.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <processgroup/sched_policy.h>
#include <scheduler/IVsyncSource.h>
#include <scheduler/PresentLatencyTracker.h>
#include "Scheduler/OneShotTimer.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/VSyncDispatchTimerQueue.h"
#include "Scheduler/VSyncPredictor.h"
#include "Scheduler/VSyncReactor.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
#include "mock/MockVSyncDispatch.h"
#include "mock/MockVSyncTracker.h"
#include "surfaceflinger_fuzzers_utils.h"
#include "surfaceflinger_scheduler_fuzzer.h"
namespace android::fuzz {
using hardware::graphics::composer::hal::PowerMode;
constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriodNsecs(),
(72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(),
(120_Hz).getPeriodNsecs()};
constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
constexpr auto kCompositionCoverage = ftl::enum_range<CompositionCoverage>();
constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
constexpr uint16_t kRandomStringLength = 256;
constexpr std::chrono::duration kSyncPeriod(16ms);
constexpr PhysicalDisplayId kDisplayId = PhysicalDisplayId::fromPort(42u);
template <typename T>
void dump(T* component, FuzzedDataProvider* fdp) {
std::string res = fdp->ConsumeRandomLengthString(kRandomStringLength);
component->dump(res);
}
inline sp<Fence> makeFakeFence() {
return sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
}
class SchedulerFuzzer {
public:
SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
void process();
private:
void fuzzRefreshRateSelection();
void fuzzRefreshRateSelector();
void fuzzPresentLatencyTracker();
void fuzzFrameTargeter();
void fuzzVSyncModulator();
void fuzzVSyncPredictor();
void fuzzVSyncReactor();
void fuzzLayerHistory();
void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch);
void fuzzVSyncDispatchTimerQueue();
void fuzzOneShotTimer();
void fuzzEventThread();
PhysicalDisplayId getPhysicalDisplayId();
FuzzedDataProvider mFdp;
std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
};
PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() {
PhysicalDisplayId internalDispId = PhysicalDisplayId::fromPort(111u);
PhysicalDisplayId externalDispId = PhysicalDisplayId::fromPort(222u);
PhysicalDisplayId randomDispId = PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint16_t>());
PhysicalDisplayId dispId64Bit = PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu);
PhysicalDisplayId displayId = mFdp.PickValueInArray<PhysicalDisplayId>(
{internalDispId, externalDispId, dispId64Bit, randomDispId});
return displayId;
}
struct EventThreadCallback : public IEventThreadCallback {
bool throttleVsync(TimePoint, uid_t) override { return false; }
Period getVsyncPeriod(uid_t) override { return kSyncPeriod; }
void resync() override {}
};
void SchedulerFuzzer::fuzzEventThread() {
mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
new scheduler::VsyncSchedule(getPhysicalDisplayId(),
std::make_shared<mock::VSyncTracker>(),
std::make_shared<mock::VSyncDispatch>(), nullptr));
EventThreadCallback callback;
std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, callback,
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
sp<EventThreadConnection> connection =
sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>());
thread->requestNextVsync(connection);
thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection);
thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
thread->registerDisplayEventConnection(connection);
thread->enableSyntheticVsync(mFdp.ConsumeBool());
dump<android::impl::EventThread>(thread.get(), &mFdp);
}
void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch) {
scheduler::VSyncDispatch::CallbackToken tmp = dispatch->registerCallback(
[&](auto, auto, auto) {
dispatch->schedule(tmp,
{.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
},
"o.o");
dispatch->schedule(tmp,
{.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
dispatch->unregisterCallback(tmp);
dispatch->cancel(tmp);
}
void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() {
auto stubTracker = std::make_shared<FuzzImplVSyncTracker>(mFdp.ConsumeIntegral<nsecs_t>());
scheduler::VSyncDispatchTimerQueue
mDispatch{std::make_unique<scheduler::ControllableClock>(), stubTracker,
mFdp.ConsumeIntegral<nsecs_t>() /*dispatchGroupThreshold*/,
mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/};
fuzzCallbackToken(&mDispatch);
dump<scheduler::VSyncDispatchTimerQueue>(&mDispatch, &mFdp);
scheduler::VSyncDispatchTimerQueueEntry entry(
"fuzz", [](auto, auto, auto) {},
mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/);
entry.update(*stubTracker, 0);
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
*stubTracker, 0);
entry.disarm();
entry.ensureNotRunning();
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
*stubTracker, 0);
auto const wakeup = entry.wakeupTime();
auto const ready = entry.readyTime();
entry.callback(entry.executing(), *wakeup, *ready);
entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp);
}
struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback {
void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
};
void SchedulerFuzzer::fuzzVSyncPredictor() {
uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX);
VsyncTrackerCallback callback;
const auto mode = ftl::as_non_null(
mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(idealPeriod)));
scheduler::VSyncPredictor tracker{mode, historySize, minimumSamplesForPrediction,
mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/,
callback};
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
tracker.setDisplayModePtr(ftl::as_non_null(
mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(period))));
for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) {
if (!tracker.needsMoreSamples()) {
break;
}
tracker.addVsyncTimestamp(now += period);
}
tracker.nextAnticipatedVSyncTimeFrom(now);
tracker.resetModel();
}
void SchedulerFuzzer::fuzzOneShotTimer() {
FakeClock* clock = new FakeClock();
std::unique_ptr<scheduler::OneShotTimer> idleTimer = std::make_unique<scheduler::OneShotTimer>(
mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
(std::chrono::milliseconds)mFdp.ConsumeIntegral<uint8_t>() /*val*/,
[] {} /*resetCallback*/, [] {} /*timeoutCallback*/, std::unique_ptr<FakeClock>(clock));
idleTimer->start();
idleTimer->reset();
idleTimer->stop();
}
void SchedulerFuzzer::fuzzLayerHistory() {
TestableSurfaceFlinger flinger;
flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(),
std::make_unique<android::mock::VSyncTracker>(),
std::make_unique<android::mock::EventThread>(),
std::make_unique<android::mock::EventThread>());
flinger.setupTimeStats(std::make_unique<android::mock::TimeStats>());
std::unique_ptr<android::renderengine::RenderEngine> renderEngine =
std::make_unique<android::renderengine::mock::RenderEngine>();
flinger.setupRenderEngine(std::move(renderEngine));
flinger.setupComposer(std::make_unique<android::Hwc2::mock::Composer>());
scheduler::TestableScheduler* scheduler = flinger.scheduler();
scheduler::LayerHistory& historyV1 = scheduler->mutableLayerHistory();
nsecs_t time1 = systemTime();
nsecs_t time2 = time1;
uint8_t historySize = mFdp.ConsumeIntegral<uint8_t>();
sp<FuzzImplLayer> layer1 = sp<FuzzImplLayer>::make(flinger.flinger());
sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger());
for (int i = 0; i < historySize; ++i) {
historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1,
scheduler::LayerHistory::LayerUpdateType::Buffer);
historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2,
scheduler::LayerHistory::LayerUpdateType::Buffer);
time1 += mFdp.PickValueInArray(kVsyncPeriods);
time2 += mFdp.PickValueInArray(kVsyncPeriods);
}
historyV1.summarize(*scheduler->refreshRateSelector(), time1);
historyV1.summarize(*scheduler->refreshRateSelector(), time2);
scheduler->createConnection(std::make_unique<android::mock::EventThread>());
scheduler::ConnectionHandle handle;
scheduler->createDisplayEventConnection(handle);
scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength);
utils::Dumper dumper(result);
scheduler->dump(dumper);
}
void SchedulerFuzzer::fuzzVSyncReactor() {
std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
scheduler::VSyncReactor reactor(kDisplayId,
std::make_unique<ClockWrapper>(
std::make_shared<FuzzImplClock>()),
*vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
false);
const auto mode = ftl::as_non_null(
mock::createDisplayMode(DisplayModeId(0),
Fps::fromPeriodNsecs(mFdp.ConsumeIntegral<nsecs_t>())));
reactor.onDisplayModeChanged(mode, mFdp.ConsumeBool());
bool periodFlushed = false; // Value does not matter, since this is an out
// param from addHwVsyncTimestamp.
reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
&periodFlushed);
const auto fence = std::make_shared<FenceTime>(makeFakeFence());
vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>());
FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>());
fence->applyTrustedSnapshot(snap);
reactor.setIgnorePresentFences(mFdp.ConsumeBool());
reactor.addPresentFence(fence);
dump<scheduler::VSyncReactor>(&reactor, &mFdp);
}
void SchedulerFuzzer::fuzzVSyncModulator() {
enum {
SF_OFFSET_LATE,
APP_OFFSET_LATE,
SF_DURATION_LATE,
APP_DURATION_LATE,
SF_OFFSET_EARLY,
APP_OFFSET_EARLY,
SF_DURATION_EARLY,
APP_DURATION_EARLY,
SF_OFFSET_EARLY_GPU,
APP_OFFSET_EARLY_GPU,
SF_DURATION_EARLY_GPU,
APP_DURATION_EARLY_GPU,
HWC_MIN_WORK_DURATION,
};
using Schedule = scheduler::TransactionSchedule;
using nanos = std::chrono::nanoseconds;
using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator;
const scheduler::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
nanos(APP_DURATION_LATE)};
const scheduler::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
const scheduler::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
nanos(APP_DURATION_EARLY_GPU)};
const scheduler::VsyncConfigSet offsets = {early, earlyGpu, late, nanos(HWC_MIN_WORK_DURATION)};
sp<FuzzImplVsyncModulator> vSyncModulator =
sp<FuzzImplVsyncModulator>::make(offsets, scheduler::Now);
(void)vSyncModulator->setVsyncConfigSet(offsets);
(void)vSyncModulator->setTransactionSchedule(Schedule::Late);
const auto token = sp<BBinder>::make();
(void)vSyncModulator->setTransactionSchedule(Schedule::EarlyStart, token);
vSyncModulator->binderDied(token);
}
void SchedulerFuzzer::fuzzRefreshRateSelection() {
TestableSurfaceFlinger flinger;
flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(),
std::make_unique<android::mock::VSyncTracker>(),
std::make_unique<android::mock::EventThread>(),
std::make_unique<android::mock::EventThread>());
sp<Client> client;
LayerCreationArgs args(flinger.flinger(), client,
mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata());
sp<Layer> layer = sp<Layer>::make(args);
layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
}
void SchedulerFuzzer::fuzzRefreshRateSelector() {
using RefreshRateSelector = scheduler::RefreshRateSelector;
using LayerRequirement = RefreshRateSelector::LayerRequirement;
using RefreshRateStats = scheduler::RefreshRateStats;
const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1);
const uint16_t maxRefreshRate =
mFdp.ConsumeIntegralInRange<uint16_t>(minRefreshRate + 1, UINT16_MAX);
const DisplayModeId modeId{mFdp.ConsumeIntegralInRange<uint8_t>(0, 10)};
DisplayModes displayModes;
for (uint16_t fps = minRefreshRate; fps < maxRefreshRate; ++fps) {
displayModes.try_emplace(modeId,
mock::createDisplayMode(modeId,
Fps::fromValue(static_cast<float>(fps))));
}
RefreshRateSelector refreshRateSelector(displayModes, modeId);
const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
refreshRateSelector.getRankedFrameRates(layers, globalSignals);
layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>());
layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values);
auto frameRateOverrides =
refreshRateSelector.getFrameRateOverrides(layers,
Fps::fromValue(
mFdp.ConsumeFloatingPoint<float>()),
globalSignals);
{
ftl::FakeGuard guard(kMainThreadContext);
refreshRateSelector.setPolicy(
RefreshRateSelector::
DisplayManagerPolicy{modeId,
{Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
refreshRateSelector.setPolicy(
RefreshRateSelector::OverridePolicy{modeId,
{Fps::fromValue(
mFdp.ConsumeFloatingPoint<float>()),
Fps::fromValue(
mFdp.ConsumeFloatingPoint<float>())}});
refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{});
refreshRateSelector.setActiveMode(modeId,
Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
}
RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue(
mFdp.ConsumeFloatingPoint<float>()),
Fps::fromValue(
mFdp.ConsumeFloatingPoint<float>()));
RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
android::mock::TimeStats timeStats;
RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
PowerMode::OFF);
const auto fpsOpt = displayModes.get(modeId).transform(
[](const DisplayModePtr& mode) { return mode->getVsyncRate(); });
refreshRateStats.setRefreshRate(*fpsOpt);
refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes));
}
void SchedulerFuzzer::fuzzPresentLatencyTracker() {
scheduler::PresentLatencyTracker tracker;
int i = 5;
while (i-- > 0) {
tracker.trackPendingFrame(getFuzzedTimePoint(mFdp),
std::make_shared<FenceTime>(makeFakeFence()));
}
}
void SchedulerFuzzer::fuzzFrameTargeter() {
scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool());
const struct VsyncSource final : scheduler::IVsyncSource {
explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
FuzzedDataProvider& fuzzer;
Period period() const { return getFuzzedDuration(fuzzer); }
TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); }
Period minFramePeriod() const { return period(); }
} vsyncSource{mFdp};
int i = 10;
while (i-- > 0) {
frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp),
.vsyncId = getFuzzedVsyncId(mFdp),
.expectedVsyncTime = getFuzzedTimePoint(mFdp),
.sfWorkDuration = getFuzzedDuration(mFdp)},
vsyncSource);
frameTargeter.setPresentFence(makeFakeFence());
frameTargeter.endFrame(
{.compositionCoverage = mFdp.PickValueInArray(kCompositionCoverage.values)});
}
}
void SchedulerFuzzer::process() {
fuzzRefreshRateSelection();
fuzzRefreshRateSelector();
fuzzPresentLatencyTracker();
fuzzFrameTargeter();
fuzzVSyncModulator();
fuzzVSyncPredictor();
fuzzVSyncReactor();
fuzzLayerHistory();
fuzzEventThread();
fuzzVSyncDispatchTimerQueue();
fuzzOneShotTimer();
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
SchedulerFuzzer schedulerFuzzer(data, size);
schedulerFuzzer.process();
return 0;
}
} // namespace android::fuzz