1*61c4878aSAndroid Build Coastguard Worker // Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker // clang-format off
16*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/internal/log_config.h" // PW_LOG_* macros must be first.
17*61c4878aSAndroid Build Coastguard Worker
18*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/fuzz/engine.h"
19*61c4878aSAndroid Build Coastguard Worker // clang-format on
20*61c4878aSAndroid Build Coastguard Worker
21*61c4878aSAndroid Build Coastguard Worker #include <algorithm>
22*61c4878aSAndroid Build Coastguard Worker #include <cctype>
23*61c4878aSAndroid Build Coastguard Worker #include <chrono>
24*61c4878aSAndroid Build Coastguard Worker #include <cinttypes>
25*61c4878aSAndroid Build Coastguard Worker #include <limits>
26*61c4878aSAndroid Build Coastguard Worker #include <mutex>
27*61c4878aSAndroid Build Coastguard Worker
28*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
29*61c4878aSAndroid Build Coastguard Worker #include "pw_bytes/span.h"
30*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
31*61c4878aSAndroid Build Coastguard Worker #include "pw_span/span.h"
32*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
33*61c4878aSAndroid Build Coastguard Worker #include "pw_string/format.h"
34*61c4878aSAndroid Build Coastguard Worker
35*61c4878aSAndroid Build Coastguard Worker namespace pw::rpc::fuzz {
36*61c4878aSAndroid Build Coastguard Worker namespace {
37*61c4878aSAndroid Build Coastguard Worker
38*61c4878aSAndroid Build Coastguard Worker using namespace std::chrono_literals;
39*61c4878aSAndroid Build Coastguard Worker
40*61c4878aSAndroid Build Coastguard Worker // Maximum number of bytes written in a single unary or stream request.
41*61c4878aSAndroid Build Coastguard Worker constexpr size_t kMaxWriteLen = MaxSafePayloadSize();
42*61c4878aSAndroid Build Coastguard Worker static_assert(kMaxWriteLen * 0x7E <= std::numeric_limits<uint16_t>::max());
43*61c4878aSAndroid Build Coastguard Worker
44*61c4878aSAndroid Build Coastguard Worker struct ActiveVisitor final {
45*61c4878aSAndroid Build Coastguard Worker using result_type = bool;
operator ()pw::rpc::fuzz::__anon735a4fdd0111::ActiveVisitor46*61c4878aSAndroid Build Coastguard Worker result_type operator()(std::monostate&) { return false; }
operator ()pw::rpc::fuzz::__anon735a4fdd0111::ActiveVisitor47*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawUnaryReceiver& call) {
48*61c4878aSAndroid Build Coastguard Worker return call.active();
49*61c4878aSAndroid Build Coastguard Worker }
operator ()pw::rpc::fuzz::__anon735a4fdd0111::ActiveVisitor50*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawClientReaderWriter& call) {
51*61c4878aSAndroid Build Coastguard Worker return call.active();
52*61c4878aSAndroid Build Coastguard Worker }
53*61c4878aSAndroid Build Coastguard Worker };
54*61c4878aSAndroid Build Coastguard Worker
55*61c4878aSAndroid Build Coastguard Worker struct CloseClientStreamVisitor final {
56*61c4878aSAndroid Build Coastguard Worker using result_type = void;
operator ()pw::rpc::fuzz::__anon735a4fdd0111::CloseClientStreamVisitor57*61c4878aSAndroid Build Coastguard Worker result_type operator()(std::monostate&) {}
operator ()pw::rpc::fuzz::__anon735a4fdd0111::CloseClientStreamVisitor58*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawUnaryReceiver&) {}
operator ()pw::rpc::fuzz::__anon735a4fdd0111::CloseClientStreamVisitor59*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawClientReaderWriter& call) {
60*61c4878aSAndroid Build Coastguard Worker call.RequestCompletion().IgnoreError();
61*61c4878aSAndroid Build Coastguard Worker }
62*61c4878aSAndroid Build Coastguard Worker };
63*61c4878aSAndroid Build Coastguard Worker
64*61c4878aSAndroid Build Coastguard Worker struct WriteVisitor final {
65*61c4878aSAndroid Build Coastguard Worker using result_type = bool;
operator ()pw::rpc::fuzz::__anon735a4fdd0111::WriteVisitor66*61c4878aSAndroid Build Coastguard Worker result_type operator()(std::monostate&) { return false; }
operator ()pw::rpc::fuzz::__anon735a4fdd0111::WriteVisitor67*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawUnaryReceiver&) { return false; }
operator ()pw::rpc::fuzz::__anon735a4fdd0111::WriteVisitor68*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawClientReaderWriter& call) {
69*61c4878aSAndroid Build Coastguard Worker if (!call.active()) {
70*61c4878aSAndroid Build Coastguard Worker return false;
71*61c4878aSAndroid Build Coastguard Worker }
72*61c4878aSAndroid Build Coastguard Worker call.Write(data).IgnoreError();
73*61c4878aSAndroid Build Coastguard Worker return true;
74*61c4878aSAndroid Build Coastguard Worker }
75*61c4878aSAndroid Build Coastguard Worker ConstByteSpan data;
76*61c4878aSAndroid Build Coastguard Worker };
77*61c4878aSAndroid Build Coastguard Worker
78*61c4878aSAndroid Build Coastguard Worker struct CancelVisitor final {
79*61c4878aSAndroid Build Coastguard Worker using result_type = void;
operator ()pw::rpc::fuzz::__anon735a4fdd0111::CancelVisitor80*61c4878aSAndroid Build Coastguard Worker result_type operator()(std::monostate&) {}
operator ()pw::rpc::fuzz::__anon735a4fdd0111::CancelVisitor81*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawUnaryReceiver& call) {
82*61c4878aSAndroid Build Coastguard Worker call.Cancel().IgnoreError();
83*61c4878aSAndroid Build Coastguard Worker }
operator ()pw::rpc::fuzz::__anon735a4fdd0111::CancelVisitor84*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawClientReaderWriter& call) {
85*61c4878aSAndroid Build Coastguard Worker call.Cancel().IgnoreError();
86*61c4878aSAndroid Build Coastguard Worker }
87*61c4878aSAndroid Build Coastguard Worker };
88*61c4878aSAndroid Build Coastguard Worker
89*61c4878aSAndroid Build Coastguard Worker struct AbandonVisitor final {
90*61c4878aSAndroid Build Coastguard Worker using result_type = void;
operator ()pw::rpc::fuzz::__anon735a4fdd0111::AbandonVisitor91*61c4878aSAndroid Build Coastguard Worker result_type operator()(std::monostate&) {}
operator ()pw::rpc::fuzz::__anon735a4fdd0111::AbandonVisitor92*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawUnaryReceiver& call) { call.Abandon(); }
operator ()pw::rpc::fuzz::__anon735a4fdd0111::AbandonVisitor93*61c4878aSAndroid Build Coastguard Worker result_type operator()(pw::rpc::RawClientReaderWriter& call) {
94*61c4878aSAndroid Build Coastguard Worker call.Abandon();
95*61c4878aSAndroid Build Coastguard Worker }
96*61c4878aSAndroid Build Coastguard Worker };
97*61c4878aSAndroid Build Coastguard Worker
98*61c4878aSAndroid Build Coastguard Worker } // namespace
99*61c4878aSAndroid Build Coastguard Worker
100*61c4878aSAndroid Build Coastguard Worker // `Action` methods.
101*61c4878aSAndroid Build Coastguard Worker
Action(uint32_t encoded)102*61c4878aSAndroid Build Coastguard Worker Action::Action(uint32_t encoded) {
103*61c4878aSAndroid Build Coastguard Worker // The first byte is used to determine the operation. The ranges used set the
104*61c4878aSAndroid Build Coastguard Worker // relative likelihood of each result, e.g. `kWait` is more likely than
105*61c4878aSAndroid Build Coastguard Worker // `kAbandon`.
106*61c4878aSAndroid Build Coastguard Worker uint32_t raw = encoded & 0xFF;
107*61c4878aSAndroid Build Coastguard Worker if (raw == 0) {
108*61c4878aSAndroid Build Coastguard Worker op = kSkip;
109*61c4878aSAndroid Build Coastguard Worker } else if (raw < 0x60) {
110*61c4878aSAndroid Build Coastguard Worker op = kWait;
111*61c4878aSAndroid Build Coastguard Worker } else if (raw < 0x80) {
112*61c4878aSAndroid Build Coastguard Worker op = kWriteUnary;
113*61c4878aSAndroid Build Coastguard Worker } else if (raw < 0xA0) {
114*61c4878aSAndroid Build Coastguard Worker op = kWriteStream;
115*61c4878aSAndroid Build Coastguard Worker } else if (raw < 0xC0) {
116*61c4878aSAndroid Build Coastguard Worker op = kCloseClientStream;
117*61c4878aSAndroid Build Coastguard Worker } else if (raw < 0xD0) {
118*61c4878aSAndroid Build Coastguard Worker op = kCancel;
119*61c4878aSAndroid Build Coastguard Worker } else if (raw < 0xE0) {
120*61c4878aSAndroid Build Coastguard Worker op = kAbandon;
121*61c4878aSAndroid Build Coastguard Worker } else if (raw < 0xF0) {
122*61c4878aSAndroid Build Coastguard Worker op = kSwap;
123*61c4878aSAndroid Build Coastguard Worker } else {
124*61c4878aSAndroid Build Coastguard Worker op = kDestroy;
125*61c4878aSAndroid Build Coastguard Worker }
126*61c4878aSAndroid Build Coastguard Worker target = ((encoded & 0xFF00) >> 8) % Fuzzer::kMaxConcurrentCalls;
127*61c4878aSAndroid Build Coastguard Worker value = encoded >> 16;
128*61c4878aSAndroid Build Coastguard Worker }
129*61c4878aSAndroid Build Coastguard Worker
Action(Op op_,size_t target_,uint16_t value_)130*61c4878aSAndroid Build Coastguard Worker Action::Action(Op op_, size_t target_, uint16_t value_)
131*61c4878aSAndroid Build Coastguard Worker : op(op_), target(target_), value(value_) {}
132*61c4878aSAndroid Build Coastguard Worker
Action(Op op_,size_t target_,char val,size_t len)133*61c4878aSAndroid Build Coastguard Worker Action::Action(Op op_, size_t target_, char val, size_t len)
134*61c4878aSAndroid Build Coastguard Worker : op(op_), target(target_) {
135*61c4878aSAndroid Build Coastguard Worker PW_ASSERT(op == kWriteUnary || op == kWriteStream);
136*61c4878aSAndroid Build Coastguard Worker value = static_cast<uint16_t>(((val % 0x80) * kMaxWriteLen) +
137*61c4878aSAndroid Build Coastguard Worker (len % kMaxWriteLen));
138*61c4878aSAndroid Build Coastguard Worker }
139*61c4878aSAndroid Build Coastguard Worker
DecodeWriteValue(uint16_t value)140*61c4878aSAndroid Build Coastguard Worker char Action::DecodeWriteValue(uint16_t value) {
141*61c4878aSAndroid Build Coastguard Worker return static_cast<char>((value / kMaxWriteLen) % 0x7F);
142*61c4878aSAndroid Build Coastguard Worker }
143*61c4878aSAndroid Build Coastguard Worker
DecodeWriteLength(uint16_t value)144*61c4878aSAndroid Build Coastguard Worker size_t Action::DecodeWriteLength(uint16_t value) {
145*61c4878aSAndroid Build Coastguard Worker return value % kMaxWriteLen;
146*61c4878aSAndroid Build Coastguard Worker }
147*61c4878aSAndroid Build Coastguard Worker
Encode() const148*61c4878aSAndroid Build Coastguard Worker uint32_t Action::Encode() const {
149*61c4878aSAndroid Build Coastguard Worker uint32_t encoded = 0;
150*61c4878aSAndroid Build Coastguard Worker switch (op) {
151*61c4878aSAndroid Build Coastguard Worker case kSkip:
152*61c4878aSAndroid Build Coastguard Worker encoded = 0x00;
153*61c4878aSAndroid Build Coastguard Worker break;
154*61c4878aSAndroid Build Coastguard Worker case kWait:
155*61c4878aSAndroid Build Coastguard Worker encoded = 0x5F;
156*61c4878aSAndroid Build Coastguard Worker break;
157*61c4878aSAndroid Build Coastguard Worker case kWriteUnary:
158*61c4878aSAndroid Build Coastguard Worker encoded = 0x7F;
159*61c4878aSAndroid Build Coastguard Worker break;
160*61c4878aSAndroid Build Coastguard Worker case kWriteStream:
161*61c4878aSAndroid Build Coastguard Worker encoded = 0x9F;
162*61c4878aSAndroid Build Coastguard Worker break;
163*61c4878aSAndroid Build Coastguard Worker case kCloseClientStream:
164*61c4878aSAndroid Build Coastguard Worker encoded = 0xBF;
165*61c4878aSAndroid Build Coastguard Worker break;
166*61c4878aSAndroid Build Coastguard Worker case kCancel:
167*61c4878aSAndroid Build Coastguard Worker encoded = 0xCF;
168*61c4878aSAndroid Build Coastguard Worker break;
169*61c4878aSAndroid Build Coastguard Worker case kAbandon:
170*61c4878aSAndroid Build Coastguard Worker encoded = 0xDF;
171*61c4878aSAndroid Build Coastguard Worker break;
172*61c4878aSAndroid Build Coastguard Worker case kSwap:
173*61c4878aSAndroid Build Coastguard Worker encoded = 0xEF;
174*61c4878aSAndroid Build Coastguard Worker break;
175*61c4878aSAndroid Build Coastguard Worker case kDestroy:
176*61c4878aSAndroid Build Coastguard Worker encoded = 0xFF;
177*61c4878aSAndroid Build Coastguard Worker break;
178*61c4878aSAndroid Build Coastguard Worker }
179*61c4878aSAndroid Build Coastguard Worker encoded |=
180*61c4878aSAndroid Build Coastguard Worker ((target < Fuzzer::kMaxConcurrentCalls ? target
181*61c4878aSAndroid Build Coastguard Worker : Fuzzer::kMaxConcurrentCalls) %
182*61c4878aSAndroid Build Coastguard Worker 0xFF)
183*61c4878aSAndroid Build Coastguard Worker << 8;
184*61c4878aSAndroid Build Coastguard Worker encoded |= (static_cast<uint32_t>(value) << 16);
185*61c4878aSAndroid Build Coastguard Worker return encoded;
186*61c4878aSAndroid Build Coastguard Worker }
187*61c4878aSAndroid Build Coastguard Worker
Log(bool verbose,size_t num_actions,const char * fmt,...) const188*61c4878aSAndroid Build Coastguard Worker void Action::Log(bool verbose, size_t num_actions, const char* fmt, ...) const {
189*61c4878aSAndroid Build Coastguard Worker if (!verbose) {
190*61c4878aSAndroid Build Coastguard Worker return;
191*61c4878aSAndroid Build Coastguard Worker }
192*61c4878aSAndroid Build Coastguard Worker char s1[16];
193*61c4878aSAndroid Build Coastguard Worker auto result = callback_id < Fuzzer::kMaxConcurrentCalls
194*61c4878aSAndroid Build Coastguard Worker ? string::Format(s1, "%-3zu", callback_id)
195*61c4878aSAndroid Build Coastguard Worker : string::Format(s1, "n/a");
196*61c4878aSAndroid Build Coastguard Worker va_list ap;
197*61c4878aSAndroid Build Coastguard Worker va_start(ap, fmt);
198*61c4878aSAndroid Build Coastguard Worker char s2[128];
199*61c4878aSAndroid Build Coastguard Worker if (result.ok()) {
200*61c4878aSAndroid Build Coastguard Worker result = string::FormatVaList(s2, fmt, ap);
201*61c4878aSAndroid Build Coastguard Worker }
202*61c4878aSAndroid Build Coastguard Worker va_end(ap);
203*61c4878aSAndroid Build Coastguard Worker if (result.ok()) {
204*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO("#%-12zu\tthread: %zu\tcallback for: %s\ttarget call: %zu\t%s",
205*61c4878aSAndroid Build Coastguard Worker num_actions,
206*61c4878aSAndroid Build Coastguard Worker thread_id,
207*61c4878aSAndroid Build Coastguard Worker s1,
208*61c4878aSAndroid Build Coastguard Worker target,
209*61c4878aSAndroid Build Coastguard Worker s2);
210*61c4878aSAndroid Build Coastguard Worker } else {
211*61c4878aSAndroid Build Coastguard Worker LogFailure(verbose, num_actions, result.status());
212*61c4878aSAndroid Build Coastguard Worker }
213*61c4878aSAndroid Build Coastguard Worker }
214*61c4878aSAndroid Build Coastguard Worker
LogFailure(bool verbose,size_t num_actions,Status status) const215*61c4878aSAndroid Build Coastguard Worker void Action::LogFailure(bool verbose, size_t num_actions, Status status) const {
216*61c4878aSAndroid Build Coastguard Worker if (verbose && !status.ok()) {
217*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO("#%-12zu\tthread: %zu\tFailed to log action: %s",
218*61c4878aSAndroid Build Coastguard Worker num_actions,
219*61c4878aSAndroid Build Coastguard Worker thread_id,
220*61c4878aSAndroid Build Coastguard Worker pw_StatusString(status));
221*61c4878aSAndroid Build Coastguard Worker }
222*61c4878aSAndroid Build Coastguard Worker }
223*61c4878aSAndroid Build Coastguard Worker
224*61c4878aSAndroid Build Coastguard Worker // FuzzyCall methods.
225*61c4878aSAndroid Build Coastguard Worker
RecordWrite(size_t num,bool append)226*61c4878aSAndroid Build Coastguard Worker void FuzzyCall::RecordWrite(size_t num, bool append) {
227*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(mutex_);
228*61c4878aSAndroid Build Coastguard Worker if (append) {
229*61c4878aSAndroid Build Coastguard Worker last_write_ += num;
230*61c4878aSAndroid Build Coastguard Worker } else {
231*61c4878aSAndroid Build Coastguard Worker last_write_ = num;
232*61c4878aSAndroid Build Coastguard Worker }
233*61c4878aSAndroid Build Coastguard Worker total_written_ += num;
234*61c4878aSAndroid Build Coastguard Worker pending_ = true;
235*61c4878aSAndroid Build Coastguard Worker }
236*61c4878aSAndroid Build Coastguard Worker
Await()237*61c4878aSAndroid Build Coastguard Worker void FuzzyCall::Await() {
238*61c4878aSAndroid Build Coastguard Worker std::unique_lock<sync::Mutex> lock(mutex_);
239*61c4878aSAndroid Build Coastguard Worker cv_.wait(lock, [this]() PW_NO_LOCK_SAFETY_ANALYSIS { return !pending_; });
240*61c4878aSAndroid Build Coastguard Worker }
241*61c4878aSAndroid Build Coastguard Worker
Notify()242*61c4878aSAndroid Build Coastguard Worker void FuzzyCall::Notify() {
243*61c4878aSAndroid Build Coastguard Worker if (pending_.exchange(false)) {
244*61c4878aSAndroid Build Coastguard Worker cv_.notify_all();
245*61c4878aSAndroid Build Coastguard Worker }
246*61c4878aSAndroid Build Coastguard Worker }
247*61c4878aSAndroid Build Coastguard Worker
Swap(FuzzyCall & other)248*61c4878aSAndroid Build Coastguard Worker void FuzzyCall::Swap(FuzzyCall& other) {
249*61c4878aSAndroid Build Coastguard Worker if (index_ == other.index_) {
250*61c4878aSAndroid Build Coastguard Worker return;
251*61c4878aSAndroid Build Coastguard Worker }
252*61c4878aSAndroid Build Coastguard Worker // Manually acquire locks in an order based on call IDs to prevent deadlock.
253*61c4878aSAndroid Build Coastguard Worker if (index_ < other.index_) {
254*61c4878aSAndroid Build Coastguard Worker mutex_.lock();
255*61c4878aSAndroid Build Coastguard Worker other.mutex_.lock();
256*61c4878aSAndroid Build Coastguard Worker } else {
257*61c4878aSAndroid Build Coastguard Worker other.mutex_.lock();
258*61c4878aSAndroid Build Coastguard Worker mutex_.lock();
259*61c4878aSAndroid Build Coastguard Worker }
260*61c4878aSAndroid Build Coastguard Worker call_.swap(other.call_);
261*61c4878aSAndroid Build Coastguard Worker std::swap(id_, other.id_);
262*61c4878aSAndroid Build Coastguard Worker pending_ = other.pending_.exchange(pending_);
263*61c4878aSAndroid Build Coastguard Worker std::swap(last_write_, other.last_write_);
264*61c4878aSAndroid Build Coastguard Worker std::swap(total_written_, other.total_written_);
265*61c4878aSAndroid Build Coastguard Worker mutex_.unlock();
266*61c4878aSAndroid Build Coastguard Worker other.mutex_.unlock();
267*61c4878aSAndroid Build Coastguard Worker cv_.notify_all();
268*61c4878aSAndroid Build Coastguard Worker other.cv_.notify_all();
269*61c4878aSAndroid Build Coastguard Worker }
270*61c4878aSAndroid Build Coastguard Worker
Reset(Variant call)271*61c4878aSAndroid Build Coastguard Worker void FuzzyCall::Reset(Variant call) {
272*61c4878aSAndroid Build Coastguard Worker {
273*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(mutex_);
274*61c4878aSAndroid Build Coastguard Worker call_ = std::move(call);
275*61c4878aSAndroid Build Coastguard Worker }
276*61c4878aSAndroid Build Coastguard Worker cv_.notify_all();
277*61c4878aSAndroid Build Coastguard Worker }
278*61c4878aSAndroid Build Coastguard Worker
Log()279*61c4878aSAndroid Build Coastguard Worker void FuzzyCall::Log() {
280*61c4878aSAndroid Build Coastguard Worker if (mutex_.try_lock_for(100ms)) {
281*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO("call %zu:", index_);
282*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO(" active: %s",
283*61c4878aSAndroid Build Coastguard Worker std::visit(ActiveVisitor(), call_) ? "true" : "false");
284*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO(" request pending: %s ", pending_ ? "true" : "false");
285*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO(" last write: %zu bytes", last_write_);
286*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO(" total written: %zu bytes", total_written_);
287*61c4878aSAndroid Build Coastguard Worker mutex_.unlock();
288*61c4878aSAndroid Build Coastguard Worker } else {
289*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN("call %zu: failed to acquire lock", index_);
290*61c4878aSAndroid Build Coastguard Worker }
291*61c4878aSAndroid Build Coastguard Worker }
292*61c4878aSAndroid Build Coastguard Worker
293*61c4878aSAndroid Build Coastguard Worker // `Fuzzer` methods.
294*61c4878aSAndroid Build Coastguard Worker
295*61c4878aSAndroid Build Coastguard Worker #define FUZZ_LOG_VERBOSE(...) \
296*61c4878aSAndroid Build Coastguard Worker if (verbose_) { \
297*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO(__VA_ARGS__); \
298*61c4878aSAndroid Build Coastguard Worker }
299*61c4878aSAndroid Build Coastguard Worker
Fuzzer(Client & client,uint32_t channel_id)300*61c4878aSAndroid Build Coastguard Worker Fuzzer::Fuzzer(Client& client, uint32_t channel_id)
301*61c4878aSAndroid Build Coastguard Worker : client_(client, channel_id),
302*61c4878aSAndroid Build Coastguard Worker timer_([this](chrono::SystemClock::time_point) {
303*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR(
304*61c4878aSAndroid Build Coastguard Worker "Workers performed %zu actions before timing out without an "
305*61c4878aSAndroid Build Coastguard Worker "update.",
306*61c4878aSAndroid Build Coastguard Worker num_actions_.load());
307*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO("Additional call details:");
308*61c4878aSAndroid Build Coastguard Worker for (auto& call : fuzzy_calls_) {
309*61c4878aSAndroid Build Coastguard Worker call.Log();
310*61c4878aSAndroid Build Coastguard Worker }
311*61c4878aSAndroid Build Coastguard Worker PW_CRASH("Fuzzer found a fatal error condition: TIMEOUT.");
312*61c4878aSAndroid Build Coastguard Worker }) {
313*61c4878aSAndroid Build Coastguard Worker for (size_t index = 0; index < kMaxConcurrentCalls; ++index) {
314*61c4878aSAndroid Build Coastguard Worker fuzzy_calls_.emplace_back(index);
315*61c4878aSAndroid Build Coastguard Worker indices_.push_back(index);
316*61c4878aSAndroid Build Coastguard Worker contexts_.push_back(CallbackContext{.id = index, .fuzzer = this});
317*61c4878aSAndroid Build Coastguard Worker }
318*61c4878aSAndroid Build Coastguard Worker }
319*61c4878aSAndroid Build Coastguard Worker
Run(uint64_t seed,size_t num_actions)320*61c4878aSAndroid Build Coastguard Worker void Fuzzer::Run(uint64_t seed, size_t num_actions) {
321*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE("Fuzzing RPC client with:");
322*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE(" num_actions: %zu", num_actions);
323*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE(" seed: %" PRIu64, seed);
324*61c4878aSAndroid Build Coastguard Worker num_actions_.store(0);
325*61c4878aSAndroid Build Coastguard Worker random::XorShiftStarRng64 rng(seed);
326*61c4878aSAndroid Build Coastguard Worker while (true) {
327*61c4878aSAndroid Build Coastguard Worker {
328*61c4878aSAndroid Build Coastguard Worker size_t actions_done = num_actions_.load();
329*61c4878aSAndroid Build Coastguard Worker if (actions_done >= num_actions) {
330*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE("Fuzzing complete; %zu actions performed.",
331*61c4878aSAndroid Build Coastguard Worker actions_done);
332*61c4878aSAndroid Build Coastguard Worker break;
333*61c4878aSAndroid Build Coastguard Worker }
334*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE("%zu actions remaining.", num_actions - actions_done);
335*61c4878aSAndroid Build Coastguard Worker }
336*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE("Generating %zu random actions.", kMaxActions);
337*61c4878aSAndroid Build Coastguard Worker pw::Vector<uint32_t, kMaxActions> actions;
338*61c4878aSAndroid Build Coastguard Worker for (size_t i = 0; i < kNumThreads; ++i) {
339*61c4878aSAndroid Build Coastguard Worker size_t num_actions_for_thread;
340*61c4878aSAndroid Build Coastguard Worker rng.GetInt(num_actions_for_thread, kMaxActionsPerThread + 1);
341*61c4878aSAndroid Build Coastguard Worker for (size_t j = 0; j < num_actions_for_thread; ++j) {
342*61c4878aSAndroid Build Coastguard Worker uint32_t encoded = 0;
343*61c4878aSAndroid Build Coastguard Worker while (!encoded) {
344*61c4878aSAndroid Build Coastguard Worker rng.GetInt(encoded);
345*61c4878aSAndroid Build Coastguard Worker }
346*61c4878aSAndroid Build Coastguard Worker actions.push_back(encoded);
347*61c4878aSAndroid Build Coastguard Worker }
348*61c4878aSAndroid Build Coastguard Worker actions.push_back(0);
349*61c4878aSAndroid Build Coastguard Worker }
350*61c4878aSAndroid Build Coastguard Worker Run(actions);
351*61c4878aSAndroid Build Coastguard Worker }
352*61c4878aSAndroid Build Coastguard Worker }
353*61c4878aSAndroid Build Coastguard Worker
Run(const pw::Vector<uint32_t> & actions)354*61c4878aSAndroid Build Coastguard Worker void Fuzzer::Run(const pw::Vector<uint32_t>& actions) {
355*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE("Starting %zu threads to perform %zu actions:",
356*61c4878aSAndroid Build Coastguard Worker kNumThreads - 1,
357*61c4878aSAndroid Build Coastguard Worker actions.size());
358*61c4878aSAndroid Build Coastguard Worker FUZZ_LOG_VERBOSE(" timeout: %lldms", timer_.timeout() / 1ms);
359*61c4878aSAndroid Build Coastguard Worker auto iter = actions.begin();
360*61c4878aSAndroid Build Coastguard Worker timer_.Restart();
361*61c4878aSAndroid Build Coastguard Worker for (size_t thread_id = 0; thread_id < kNumThreads; ++thread_id) {
362*61c4878aSAndroid Build Coastguard Worker pw::Vector<uint32_t, kMaxActionsPerThread> thread_actions;
363*61c4878aSAndroid Build Coastguard Worker while (thread_actions.size() < kMaxActionsPerThread &&
364*61c4878aSAndroid Build Coastguard Worker iter != actions.end()) {
365*61c4878aSAndroid Build Coastguard Worker uint32_t encoded = *iter++;
366*61c4878aSAndroid Build Coastguard Worker if (!encoded) {
367*61c4878aSAndroid Build Coastguard Worker break;
368*61c4878aSAndroid Build Coastguard Worker }
369*61c4878aSAndroid Build Coastguard Worker thread_actions.push_back(encoded);
370*61c4878aSAndroid Build Coastguard Worker }
371*61c4878aSAndroid Build Coastguard Worker if (thread_id == 0) {
372*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(mutex_);
373*61c4878aSAndroid Build Coastguard Worker callback_actions_ = std::move(thread_actions);
374*61c4878aSAndroid Build Coastguard Worker callback_iterator_ = callback_actions_.begin();
375*61c4878aSAndroid Build Coastguard Worker } else {
376*61c4878aSAndroid Build Coastguard Worker threads_.emplace_back(
377*61c4878aSAndroid Build Coastguard Worker [this, thread_id, actions_to_perform = std::move(thread_actions)]() {
378*61c4878aSAndroid Build Coastguard Worker for (const auto& encoded : actions_to_perform) {
379*61c4878aSAndroid Build Coastguard Worker Action action(encoded);
380*61c4878aSAndroid Build Coastguard Worker action.set_thread_id(thread_id);
381*61c4878aSAndroid Build Coastguard Worker Perform(action);
382*61c4878aSAndroid Build Coastguard Worker }
383*61c4878aSAndroid Build Coastguard Worker });
384*61c4878aSAndroid Build Coastguard Worker }
385*61c4878aSAndroid Build Coastguard Worker }
386*61c4878aSAndroid Build Coastguard Worker for (auto& t : threads_) {
387*61c4878aSAndroid Build Coastguard Worker t.join();
388*61c4878aSAndroid Build Coastguard Worker }
389*61c4878aSAndroid Build Coastguard Worker for (auto& fuzzy_call : fuzzy_calls_) {
390*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Reset();
391*61c4878aSAndroid Build Coastguard Worker }
392*61c4878aSAndroid Build Coastguard Worker timer_.Cancel();
393*61c4878aSAndroid Build Coastguard Worker }
394*61c4878aSAndroid Build Coastguard Worker
Perform(const Action & action)395*61c4878aSAndroid Build Coastguard Worker void Fuzzer::Perform(const Action& action) {
396*61c4878aSAndroid Build Coastguard Worker FuzzyCall& fuzzy_call = FindCall(action.target);
397*61c4878aSAndroid Build Coastguard Worker switch (action.op) {
398*61c4878aSAndroid Build Coastguard Worker case Action::kSkip: {
399*61c4878aSAndroid Build Coastguard Worker if (action.thread_id == 0) {
400*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_, ++num_actions_, "Callback chain completed");
401*61c4878aSAndroid Build Coastguard Worker }
402*61c4878aSAndroid Build Coastguard Worker break;
403*61c4878aSAndroid Build Coastguard Worker }
404*61c4878aSAndroid Build Coastguard Worker case Action::kWait: {
405*61c4878aSAndroid Build Coastguard Worker if (action.callback_id == action.target) {
406*61c4878aSAndroid Build Coastguard Worker // Don't wait in a callback of the target call.
407*61c4878aSAndroid Build Coastguard Worker break;
408*61c4878aSAndroid Build Coastguard Worker }
409*61c4878aSAndroid Build Coastguard Worker if (fuzzy_call.pending()) {
410*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_, ++num_actions_, "Waiting for call.");
411*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Await();
412*61c4878aSAndroid Build Coastguard Worker }
413*61c4878aSAndroid Build Coastguard Worker break;
414*61c4878aSAndroid Build Coastguard Worker }
415*61c4878aSAndroid Build Coastguard Worker case Action::kWriteUnary:
416*61c4878aSAndroid Build Coastguard Worker case Action::kWriteStream: {
417*61c4878aSAndroid Build Coastguard Worker if (action.callback_id == action.target) {
418*61c4878aSAndroid Build Coastguard Worker // Don't create a new call from the call's own callback.
419*61c4878aSAndroid Build Coastguard Worker break;
420*61c4878aSAndroid Build Coastguard Worker }
421*61c4878aSAndroid Build Coastguard Worker char buf[kMaxWriteLen];
422*61c4878aSAndroid Build Coastguard Worker char val = Action::DecodeWriteValue(action.value);
423*61c4878aSAndroid Build Coastguard Worker size_t len = Action::DecodeWriteLength(action.value);
424*61c4878aSAndroid Build Coastguard Worker memset(buf, val, len);
425*61c4878aSAndroid Build Coastguard Worker if (verbose_) {
426*61c4878aSAndroid Build Coastguard Worker char msg_buf[64];
427*61c4878aSAndroid Build Coastguard Worker span msg(msg_buf);
428*61c4878aSAndroid Build Coastguard Worker auto result = string::Format(
429*61c4878aSAndroid Build Coastguard Worker msg,
430*61c4878aSAndroid Build Coastguard Worker "Writing %s request of ",
431*61c4878aSAndroid Build Coastguard Worker action.op == Action::kWriteUnary ? "unary" : "stream");
432*61c4878aSAndroid Build Coastguard Worker if (result.ok()) {
433*61c4878aSAndroid Build Coastguard Worker size_t off = result.size();
434*61c4878aSAndroid Build Coastguard Worker result = string::Format(
435*61c4878aSAndroid Build Coastguard Worker msg.subspan(off),
436*61c4878aSAndroid Build Coastguard Worker isprint(val) ? "['%c'; %zu]." : "['\\x%02x'; %zu].",
437*61c4878aSAndroid Build Coastguard Worker val,
438*61c4878aSAndroid Build Coastguard Worker len);
439*61c4878aSAndroid Build Coastguard Worker }
440*61c4878aSAndroid Build Coastguard Worker size_t num_actions = ++num_actions_;
441*61c4878aSAndroid Build Coastguard Worker if (result.ok()) {
442*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_, num_actions, "%s", msg.data());
443*61c4878aSAndroid Build Coastguard Worker } else if (verbose_) {
444*61c4878aSAndroid Build Coastguard Worker action.LogFailure(verbose_, num_actions, result.status());
445*61c4878aSAndroid Build Coastguard Worker }
446*61c4878aSAndroid Build Coastguard Worker }
447*61c4878aSAndroid Build Coastguard Worker bool append = false;
448*61c4878aSAndroid Build Coastguard Worker if (action.op == Action::kWriteUnary) {
449*61c4878aSAndroid Build Coastguard Worker // Send a unary request.
450*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Reset(client_.UnaryEcho(
451*61c4878aSAndroid Build Coastguard Worker as_bytes(span(buf, len)),
452*61c4878aSAndroid Build Coastguard Worker /* on completed */
453*61c4878aSAndroid Build Coastguard Worker [context = GetContext(action.target)](ConstByteSpan, Status) {
454*61c4878aSAndroid Build Coastguard Worker context->fuzzer->OnCompleted(context->id);
455*61c4878aSAndroid Build Coastguard Worker },
456*61c4878aSAndroid Build Coastguard Worker /* on error */
457*61c4878aSAndroid Build Coastguard Worker [context = GetContext(action.target)](Status status) {
458*61c4878aSAndroid Build Coastguard Worker context->fuzzer->OnError(context->id, status);
459*61c4878aSAndroid Build Coastguard Worker }));
460*61c4878aSAndroid Build Coastguard Worker
461*61c4878aSAndroid Build Coastguard Worker } else if (fuzzy_call.Visit(
462*61c4878aSAndroid Build Coastguard Worker WriteVisitor{.data = as_bytes(span(buf, len))})) {
463*61c4878aSAndroid Build Coastguard Worker // Append to an existing stream
464*61c4878aSAndroid Build Coastguard Worker append = true;
465*61c4878aSAndroid Build Coastguard Worker } else {
466*61c4878aSAndroid Build Coastguard Worker // .Open a new stream.
467*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Reset(client_.BidirectionalEcho(
468*61c4878aSAndroid Build Coastguard Worker /* on next */
469*61c4878aSAndroid Build Coastguard Worker [context = GetContext(action.target)](ConstByteSpan) {
470*61c4878aSAndroid Build Coastguard Worker context->fuzzer->OnNext(context->id);
471*61c4878aSAndroid Build Coastguard Worker },
472*61c4878aSAndroid Build Coastguard Worker /* on completed */
473*61c4878aSAndroid Build Coastguard Worker [context = GetContext(action.target)](Status) {
474*61c4878aSAndroid Build Coastguard Worker context->fuzzer->OnCompleted(context->id);
475*61c4878aSAndroid Build Coastguard Worker },
476*61c4878aSAndroid Build Coastguard Worker /* on error */
477*61c4878aSAndroid Build Coastguard Worker [context = GetContext(action.target)](Status status) {
478*61c4878aSAndroid Build Coastguard Worker context->fuzzer->OnError(context->id, status);
479*61c4878aSAndroid Build Coastguard Worker }));
480*61c4878aSAndroid Build Coastguard Worker }
481*61c4878aSAndroid Build Coastguard Worker fuzzy_call.RecordWrite(len, append);
482*61c4878aSAndroid Build Coastguard Worker break;
483*61c4878aSAndroid Build Coastguard Worker }
484*61c4878aSAndroid Build Coastguard Worker case Action::kCloseClientStream:
485*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_, ++num_actions_, "Closing stream.");
486*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Visit(CloseClientStreamVisitor());
487*61c4878aSAndroid Build Coastguard Worker break;
488*61c4878aSAndroid Build Coastguard Worker case Action::kCancel:
489*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_, ++num_actions_, "Canceling call.");
490*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Visit(CancelVisitor());
491*61c4878aSAndroid Build Coastguard Worker break;
492*61c4878aSAndroid Build Coastguard Worker case Action::kAbandon: {
493*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_, ++num_actions_, "Abandoning call.");
494*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Visit(AbandonVisitor());
495*61c4878aSAndroid Build Coastguard Worker break;
496*61c4878aSAndroid Build Coastguard Worker }
497*61c4878aSAndroid Build Coastguard Worker case Action::kSwap: {
498*61c4878aSAndroid Build Coastguard Worker size_t other_target = action.value % kMaxConcurrentCalls;
499*61c4878aSAndroid Build Coastguard Worker if (action.callback_id == action.target ||
500*61c4878aSAndroid Build Coastguard Worker action.callback_id == other_target) {
501*61c4878aSAndroid Build Coastguard Worker // Don't move a call from within its own callback.
502*61c4878aSAndroid Build Coastguard Worker break;
503*61c4878aSAndroid Build Coastguard Worker }
504*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_,
505*61c4878aSAndroid Build Coastguard Worker ++num_actions_,
506*61c4878aSAndroid Build Coastguard Worker "Swapping call with call %zu.",
507*61c4878aSAndroid Build Coastguard Worker other_target);
508*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(mutex_);
509*61c4878aSAndroid Build Coastguard Worker FuzzyCall& other = FindCallLocked(other_target);
510*61c4878aSAndroid Build Coastguard Worker std::swap(indices_[fuzzy_call.id()], indices_[other.id()]);
511*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Swap(other);
512*61c4878aSAndroid Build Coastguard Worker break;
513*61c4878aSAndroid Build Coastguard Worker }
514*61c4878aSAndroid Build Coastguard Worker case Action::kDestroy: {
515*61c4878aSAndroid Build Coastguard Worker if (action.callback_id == action.target) {
516*61c4878aSAndroid Build Coastguard Worker // Don't destroy a call from within its own callback.
517*61c4878aSAndroid Build Coastguard Worker break;
518*61c4878aSAndroid Build Coastguard Worker }
519*61c4878aSAndroid Build Coastguard Worker action.Log(verbose_, ++num_actions_, "Destroying call.");
520*61c4878aSAndroid Build Coastguard Worker fuzzy_call.Reset();
521*61c4878aSAndroid Build Coastguard Worker break;
522*61c4878aSAndroid Build Coastguard Worker }
523*61c4878aSAndroid Build Coastguard Worker default:
524*61c4878aSAndroid Build Coastguard Worker break;
525*61c4878aSAndroid Build Coastguard Worker }
526*61c4878aSAndroid Build Coastguard Worker timer_.Restart();
527*61c4878aSAndroid Build Coastguard Worker }
528*61c4878aSAndroid Build Coastguard Worker
OnNext(size_t callback_id)529*61c4878aSAndroid Build Coastguard Worker void Fuzzer::OnNext(size_t callback_id) { FindCall(callback_id).Notify(); }
530*61c4878aSAndroid Build Coastguard Worker
OnCompleted(size_t callback_id)531*61c4878aSAndroid Build Coastguard Worker void Fuzzer::OnCompleted(size_t callback_id) {
532*61c4878aSAndroid Build Coastguard Worker uint32_t encoded = 0;
533*61c4878aSAndroid Build Coastguard Worker {
534*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(mutex_);
535*61c4878aSAndroid Build Coastguard Worker if (callback_iterator_ != callback_actions_.end()) {
536*61c4878aSAndroid Build Coastguard Worker encoded = *callback_iterator_++;
537*61c4878aSAndroid Build Coastguard Worker }
538*61c4878aSAndroid Build Coastguard Worker }
539*61c4878aSAndroid Build Coastguard Worker Action action(encoded);
540*61c4878aSAndroid Build Coastguard Worker action.set_callback_id(callback_id);
541*61c4878aSAndroid Build Coastguard Worker Perform(action);
542*61c4878aSAndroid Build Coastguard Worker FindCall(callback_id).Notify();
543*61c4878aSAndroid Build Coastguard Worker }
544*61c4878aSAndroid Build Coastguard Worker
OnError(size_t callback_id,Status status)545*61c4878aSAndroid Build Coastguard Worker void Fuzzer::OnError(size_t callback_id, Status status) {
546*61c4878aSAndroid Build Coastguard Worker FuzzyCall& call = FindCall(callback_id);
547*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN("Call %zu received an error from the server: %s",
548*61c4878aSAndroid Build Coastguard Worker call.id(),
549*61c4878aSAndroid Build Coastguard Worker pw_StatusString(status));
550*61c4878aSAndroid Build Coastguard Worker call.Notify();
551*61c4878aSAndroid Build Coastguard Worker }
552*61c4878aSAndroid Build Coastguard Worker
553*61c4878aSAndroid Build Coastguard Worker } // namespace pw::rpc::fuzz
554