1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/notreached.h"
11 #include "base/task/single_thread_task_runner.h"
12 #include "base/test/task_environment.h"
13 #include "base/test/test_timeouts.h"
14 #include "components/leveldb_proto/testing/fake_db.h"
15 #include "media/capabilities/webrtc_video_stats.pb.h"
16 #include "media/capabilities/webrtc_video_stats_db_impl.h"
17 #include "media/mojo/mojom/webrtc_video_perf.mojom-mojolpm.h"
18 #include "media/mojo/services/webrtc_video_perf_history.h"
19 #include "media/mojo/services/webrtc_video_perf_mojolpm_fuzzer.pb.h"
20 #include "media/mojo/services/webrtc_video_perf_recorder.h"
21 #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
25 // Helper class to call private constructor of friend class.
26 class WebrtcVideoPerfLPMFuzzerHelper {
28 static std::unique_ptr<WebrtcVideoStatsDBImpl> CreateWebrtcVideoStatsDbImpl(
29 std::unique_ptr<leveldb_proto::ProtoDatabase<WebrtcVideoStatsEntryProto>>
31 return base::WrapUnique(new WebrtcVideoStatsDBImpl(std::move(proto_db)));
39 // The call to CommandLine::Init is needed so that TestTimeouts::Initialize
41 bool success = base::CommandLine::Init(0, nullptr);
43 // TaskEnvironment requires TestTimeouts initialization to watch for
44 // problematic long-running tasks.
45 TestTimeouts::Initialize();
47 // Mark this thread as an IO_THREAD with MOCK_TIME, and ensure that Now()
48 // is driven from the same mock clock.
49 task_environment = std::make_unique<base::test::TaskEnvironment>(
50 base::test::TaskEnvironment::MainThreadType::IO,
51 base::test::TaskEnvironment::TimeSource::MOCK_TIME);
54 // This allows us to mock time for all threads.
55 std::unique_ptr<base::test::TaskEnvironment> task_environment;
58 InitGlobals* init_globals = new InitGlobals();
60 base::test::TaskEnvironment& GetEnvironment() {
61 return *init_globals->task_environment;
64 scoped_refptr<base::SingleThreadTaskRunner> GetFuzzerTaskRunner() {
65 return GetEnvironment().GetMainThreadTaskRunner();
68 // This in-memory database uses the FakeDB proto implementation as the
69 // underlying storage. The underlying FakeDB class requires that all callbacks
70 // are triggered manually. This class is used as a convenience class triggering
71 // the callbacks with success=true.
72 class InMemoryWebrtcVideoPerfDb
73 : public leveldb_proto::test::FakeDB<WebrtcVideoStatsEntryProto> {
75 explicit InMemoryWebrtcVideoPerfDb(EntryMap* db) : FakeDB(db) {}
77 // Partial ProtoDatabase implementation.
78 void Init(leveldb_proto::Callbacks::InitStatusCallback callback) override {
79 FakeDB::Init(std::move(callback));
80 InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
84 const std::string& key,
85 typename leveldb_proto::Callbacks::Internal<
86 WebrtcVideoStatsEntryProto>::GetCallback callback) override {
87 FakeDB::GetEntry(key, std::move(callback));
92 void LoadKeysAndEntriesWhile(
93 const std::string& start,
94 const leveldb_proto::KeyIteratorController& controller,
95 typename leveldb_proto::Callbacks::Internal<WebrtcVideoStatsEntryProto>::
96 LoadKeysAndEntriesCallback callback) override {
97 FakeDB::LoadKeysAndEntriesWhile(start, controller, std::move(callback));
103 std::unique_ptr<typename ProtoDatabase<
104 WebrtcVideoStatsEntryProto>::KeyEntryVector> entries_to_save,
105 std::unique_ptr<std::vector<std::string>> keys_to_remove,
106 leveldb_proto::Callbacks::UpdateCallback callback) override {
107 FakeDB::UpdateEntries(std::move(entries_to_save), std::move(keys_to_remove),
108 std::move(callback));
110 UpdateCallback(true);
113 void UpdateEntriesWithRemoveFilter(
114 std::unique_ptr<typename leveldb_proto::Util::Internal<
115 WebrtcVideoStatsEntryProto>::KeyEntryVector> entries_to_save,
116 const leveldb_proto::KeyFilter& filter,
117 leveldb_proto::Callbacks::UpdateCallback callback) override {
118 FakeDB::UpdateEntriesWithRemoveFilter(std::move(entries_to_save), filter,
119 std::move(callback));
121 UpdateCallback(true);
125 class WebrtcVideoPerfLPMFuzzer {
127 WebrtcVideoPerfLPMFuzzer(
128 const fuzzing::webrtc_video_perf::proto::Testcase& testcase)
129 : testcase_(testcase) {
130 // Create all objects that are needed and connect everything.
131 in_memory_db_ = new InMemoryWebrtcVideoPerfDb(&in_memory_db_map_);
132 std::unique_ptr<WebrtcVideoStatsDBImpl> stats_db =
133 WebrtcVideoPerfLPMFuzzerHelper::CreateWebrtcVideoStatsDbImpl(
134 std::unique_ptr<InMemoryWebrtcVideoPerfDb>(in_memory_db_));
136 std::make_unique<WebrtcVideoPerfHistory>(std::move(stats_db));
137 perf_recorder_ = std::make_unique<WebrtcVideoPerfRecorder>(
138 perf_history_->GetSaveCallback());
142 const auto& action = testcase_->actions(action_index_);
143 switch (action.action_case()) {
144 case fuzzing::webrtc_video_perf::proto::Action::kUpdateRecord: {
145 const auto& update_record = action.update_record();
146 auto features_ptr = media::mojom::WebrtcPredictionFeatures::New();
147 auto video_stats_ptr = media::mojom::WebrtcVideoStats::New();
148 mojolpm::FromProto(update_record.features(), features_ptr);
149 mojolpm::FromProto(update_record.video_stats(), video_stats_ptr);
150 perf_recorder_->UpdateRecord(std::move(features_ptr),
151 std::move(video_stats_ptr));
154 case fuzzing::webrtc_video_perf::proto::Action::kGetPerfInfo: {
155 const auto& get_perf_info = action.get_perf_info();
156 auto features_ptr = media::mojom::WebrtcPredictionFeatures::New();
157 mojolpm::FromProto(get_perf_info.features(), features_ptr);
158 perf_history_->GetPerfInfo(std::move(features_ptr),
159 get_perf_info.frames_per_second(),
170 bool IsFinished() { return action_index_ >= testcase_->actions_size(); }
173 const raw_ref<const fuzzing::webrtc_video_perf::proto::Testcase> testcase_;
174 int action_index_ = 0;
177 InMemoryWebrtcVideoPerfDb::EntryMap in_memory_db_map_;
178 // Proto buffer database implementation that uses `in_memory_db_map_` as
180 raw_ptr<InMemoryWebrtcVideoPerfDb> in_memory_db_;
181 std::unique_ptr<WebrtcVideoPerfHistory> perf_history_;
182 std::unique_ptr<WebrtcVideoPerfRecorder> perf_recorder_;
185 void NextAction(WebrtcVideoPerfLPMFuzzer* testcase,
186 base::OnceClosure fuzzer_run_loop) {
187 if (!testcase->IsFinished()) {
188 testcase->NextAction();
189 GetFuzzerTaskRunner()->PostTask(
190 FROM_HERE, base::BindOnce(NextAction, base::Unretained(testcase),
191 std::move(fuzzer_run_loop)));
193 std::move(fuzzer_run_loop).Run();
197 void RunTestcase(WebrtcVideoPerfLPMFuzzer* testcase) {
198 base::RunLoop fuzzer_run_loop;
199 GetFuzzerTaskRunner()->PostTask(
200 FROM_HERE, base::BindOnce(NextAction, base::Unretained(testcase),
201 fuzzer_run_loop.QuitClosure()));
202 // Make sure that all callbacks have completed.
203 constexpr base::TimeDelta kTimeout = base::Seconds(5);
204 GetEnvironment().FastForwardBy(kTimeout);
205 fuzzer_run_loop.Run();
210 DEFINE_BINARY_PROTO_FUZZER(
211 const fuzzing::webrtc_video_perf::proto::Testcase& testcase) {
212 if (!testcase.actions_size()) {
216 WebrtcVideoPerfLPMFuzzer webtc_video_perf_fuzzer_instance(testcase);
217 base::RunLoop main_run_loop;
219 GetFuzzerTaskRunner()->PostTaskAndReply(
221 base::BindOnce(RunTestcase,
222 base::Unretained(&webtc_video_perf_fuzzer_instance)),
223 main_run_loop.QuitClosure());