405 lines
18 KiB
C++
405 lines
18 KiB
C++
/*
|
|
* 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/DispSyncSource.h"
|
|
#include "Scheduler/OneShotTimer.h"
|
|
#include "Scheduler/VSyncDispatchTimerQueue.h"
|
|
#include "Scheduler/VSyncPredictor.h"
|
|
#include "Scheduler/VSyncReactor.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::RefreshRateConfigs::LayerVoteType>();
|
|
|
|
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);
|
|
|
|
template <typename T>
|
|
void dump(T* component, FuzzedDataProvider* fdp) {
|
|
std::string res = fdp->ConsumeRandomLengthString(kRandomStringLength);
|
|
component->dump(res);
|
|
}
|
|
|
|
class SchedulerFuzzer : private VSyncSource::Callback {
|
|
public:
|
|
SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
|
|
void process();
|
|
|
|
private:
|
|
void fuzzRefreshRateSelection();
|
|
void fuzzRefreshRateConfigs();
|
|
void fuzzVSyncModulator();
|
|
void fuzzVSyncPredictor();
|
|
void fuzzVSyncReactor();
|
|
void fuzzLayerHistory();
|
|
void fuzzDispSyncSource();
|
|
void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch);
|
|
void fuzzVSyncDispatchTimerQueue();
|
|
void fuzzOneShotTimer();
|
|
void fuzzEventThread();
|
|
PhysicalDisplayId getPhysicalDisplayId();
|
|
|
|
FuzzedDataProvider mFdp;
|
|
|
|
protected:
|
|
void onVSyncEvent(nsecs_t /* when */, VSyncSource::VSyncData) {}
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
void SchedulerFuzzer::fuzzEventThread() {
|
|
const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
|
|
std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
|
|
android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr,
|
|
nullptr, nullptr, getVsyncPeriod);
|
|
|
|
thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
|
|
sp<EventThreadConnection> connection =
|
|
new EventThreadConnection(thread.get(), mFdp.ConsumeIntegral<uint16_t>(), nullptr,
|
|
{} /*eventRegistration*/);
|
|
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->onScreenAcquired();
|
|
thread->onScreenReleased();
|
|
dump<android::impl::EventThread>(thread.get(), &mFdp);
|
|
}
|
|
|
|
void SchedulerFuzzer::fuzzDispSyncSource() {
|
|
std::unique_ptr<FuzzImplVSyncDispatch> vSyncDispatch =
|
|
std::make_unique<FuzzImplVSyncDispatch>();
|
|
std::unique_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_unique<FuzzImplVSyncTracker>();
|
|
std::unique_ptr<scheduler::DispSyncSource> dispSyncSource = std::make_unique<
|
|
scheduler::DispSyncSource>(*vSyncDispatch, *vSyncTracker,
|
|
(std::chrono::nanoseconds)
|
|
mFdp.ConsumeIntegral<uint64_t>() /*workDuration*/,
|
|
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()
|
|
/*readyDuration*/,
|
|
mFdp.ConsumeBool(),
|
|
mFdp.ConsumeRandomLengthString(kRandomStringLength).c_str());
|
|
dispSyncSource->setVSyncEnabled(true);
|
|
dispSyncSource->setCallback(this);
|
|
dispSyncSource->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), 0ns);
|
|
dump<scheduler::DispSyncSource>(dispSyncSource.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() {
|
|
FuzzImplVSyncTracker stubTracker{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);
|
|
}
|
|
|
|
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);
|
|
scheduler::VSyncPredictor tracker{mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
|
|
minimumSamplesForPrediction,
|
|
mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
|
|
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
|
|
tracker.setPeriod(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 = new FuzzImplLayer(flinger.flinger());
|
|
sp<FuzzImplLayer> layer2 = new FuzzImplLayer(flinger.flinger());
|
|
|
|
for (int i = 0; i < historySize; ++i) {
|
|
historyV1.record(layer1.get(), time1, time1,
|
|
scheduler::LayerHistory::LayerUpdateType::Buffer);
|
|
historyV1.record(layer2.get(), time2, time2,
|
|
scheduler::LayerHistory::LayerUpdateType::Buffer);
|
|
time1 += mFdp.PickValueInArray(kVsyncPeriods);
|
|
time2 += mFdp.PickValueInArray(kVsyncPeriods);
|
|
}
|
|
historyV1.summarize(*scheduler->refreshRateConfigs(), time1);
|
|
historyV1.summarize(*scheduler->refreshRateConfigs(), 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>());
|
|
|
|
dump<scheduler::TestableScheduler>(scheduler, &mFdp);
|
|
}
|
|
|
|
void SchedulerFuzzer::fuzzVSyncReactor() {
|
|
std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
|
|
scheduler::VSyncReactor reactor(std::make_unique<ClockWrapper>(
|
|
std::make_shared<FuzzImplClock>()),
|
|
*vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
|
|
false);
|
|
|
|
reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>());
|
|
bool periodFlushed = mFdp.ConsumeBool();
|
|
reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
|
|
reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
|
|
&periodFlushed);
|
|
sp<Fence> fence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
|
|
std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
|
|
vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>());
|
|
FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>());
|
|
ft->applyTrustedSnapshot(snap);
|
|
reactor.setIgnorePresentFences(mFdp.ConsumeBool());
|
|
reactor.addPresentFence(ft);
|
|
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 VsyncModulator = scheduler::VsyncModulator;
|
|
using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator;
|
|
const VsyncModulator::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
|
|
nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
|
|
const VsyncModulator::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
|
|
nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
|
|
const VsyncModulator::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE,
|
|
nanos(SF_DURATION_EARLY_GPU),
|
|
nanos(APP_DURATION_EARLY_GPU)};
|
|
const VsyncModulator::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 = new BufferQueueLayer(args);
|
|
|
|
layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
|
|
}
|
|
|
|
void SchedulerFuzzer::fuzzRefreshRateConfigs() {
|
|
using RefreshRateConfigs = scheduler::RefreshRateConfigs;
|
|
using LayerRequirement = RefreshRateConfigs::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))));
|
|
}
|
|
|
|
RefreshRateConfigs refreshRateConfigs(displayModes, modeId);
|
|
|
|
const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
|
|
std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
|
|
|
|
refreshRateConfigs.getBestRefreshRate(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 =
|
|
refreshRateConfigs.getFrameRateOverrides(layers,
|
|
Fps::fromValue(
|
|
mFdp.ConsumeFloatingPoint<float>()),
|
|
globalSignals);
|
|
|
|
refreshRateConfigs.setDisplayManagerPolicy(
|
|
{modeId,
|
|
{Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
|
|
Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
|
|
refreshRateConfigs.setActiveModeId(modeId);
|
|
|
|
RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
|
|
mFdp.ConsumeFloatingPoint<float>()),
|
|
Fps::fromValue(
|
|
mFdp.ConsumeFloatingPoint<float>()));
|
|
RefreshRateConfigs::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, [](const auto& mode) { return mode->getFps(); });
|
|
refreshRateStats.setRefreshRate(*fpsOpt);
|
|
|
|
refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes));
|
|
}
|
|
|
|
void SchedulerFuzzer::process() {
|
|
fuzzRefreshRateSelection();
|
|
fuzzRefreshRateConfigs();
|
|
fuzzVSyncModulator();
|
|
fuzzVSyncPredictor();
|
|
fuzzVSyncReactor();
|
|
fuzzLayerHistory();
|
|
fuzzDispSyncSource();
|
|
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
|