xref: /aosp_15_r20/test/dittosuite/src/instruction_factory.cpp (revision 6fa2df46f119dce7527f5beb2814eca0e6f886ac)
1 // Copyright (C) 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <ditto/instruction_factory.h>
16 
17 #include <fcntl.h>
18 #include <sys/types.h>
19 
20 #include <random>
21 
22 #include <ditto/binder_request.h>
23 #include <ditto/binder_service.h>
24 #include <ditto/close_file.h>
25 #include <ditto/cpu_work.h>
26 #include <ditto/delete_file.h>
27 #include <ditto/instruction_set.h>
28 #include <ditto/invalidate_cache.h>
29 #include <ditto/lock.h>
30 #include <ditto/logger.h>
31 #include <ditto/memory_allocation.h>
32 #include <ditto/multiprocessing.h>
33 #include <ditto/multithreading.h>
34 #include <ditto/multithreading_utils.h>
35 #include <ditto/open_file.h>
36 #include <ditto/read_directory.h>
37 #include <ditto/read_write_file.h>
38 #include <ditto/resize_file.h>
39 #include <ditto/shared_variables.h>
40 #include <ditto/syscall.h>
41 
42 namespace dittosuite {
43 typedef dittosuiteproto::Instruction::InstructionOneofCase InstructionType;
44 typedef dittosuiteproto::BinderRequest::ServiceOneofCase RequestService;
45 typedef dittosuiteproto::CpuWork::TypeCase CpuWorkType;
46 
CreateFromProtoInstructionSet(const dittosuite::Instruction::Params & instruction_params,const std::list<int> & thread_ids,const dittosuiteproto::InstructionSet & proto_instruction_set)47 std::unique_ptr<InstructionSet> InstructionFactory::CreateFromProtoInstructionSet(
48     const dittosuite::Instruction::Params& instruction_params, const std::list<int>& thread_ids,
49     const dittosuiteproto::InstructionSet& proto_instruction_set) {
50   std::vector<std::unique_ptr<Instruction>> instructions;
51   for (const auto& instruction : proto_instruction_set.instructions()) {
52     instructions.push_back(
53         InstructionFactory::CreateFromProtoInstruction(thread_ids, instruction));
54   }
55 
56   if (proto_instruction_set.has_iterate_options()) {
57     const auto& options = proto_instruction_set.iterate_options();
58 
59     int list_key = SharedVariables::GetKey(thread_ids, options.list_name());
60     int item_key = SharedVariables::GetKey(thread_ids, options.item_name());
61     auto access_order = ConvertOrder(options.access_order());
62     auto reseeding = ConvertReseeding(options.reseeding());
63 
64     uint32_t seed = options.seed();
65     if (!options.has_seed()) {
66       seed = time(nullptr);
67     }
68 
69     return std::make_unique<InstructionSet>(instruction_params, std::move(instructions), list_key,
70                                             item_key, access_order, reseeding, seed);
71   } else {
72     return std::make_unique<InstructionSet>(instruction_params, std::move(instructions));
73   }
74 }
75 
CreateFromProtoInstruction(const std::list<int> & thread_ids,const dittosuiteproto::Instruction & proto_instruction)76 std::unique_ptr<Instruction> InstructionFactory::CreateFromProtoInstruction(
77     const std::list<int>& thread_ids, const dittosuiteproto::Instruction& proto_instruction) {
78   Instruction::Params instruction_params(Syscall::GetSyscall(), proto_instruction.repeat(),
79                                          proto_instruction.period_us(),
80                                          proto_instruction.offset_us());
81 
82   switch (proto_instruction.instruction_oneof_case()) {
83     case InstructionType::kInstructionSet: {
84       return InstructionFactory::CreateFromProtoInstructionSet(instruction_params, thread_ids,
85                                                                proto_instruction.instruction_set());
86     }
87     case InstructionType::kOpenFile: {
88       const auto& options = proto_instruction.open_file();
89 
90       int fd_key = -1;
91       if (options.has_output_fd()) {
92         fd_key = SharedVariables::GetKey(thread_ids, options.output_fd());
93       }
94 
95       dittosuite::OpenFile::AccessMode access_mode;
96       {
97         switch (options.access_mode()) {
98           case dittosuiteproto::AccessMode::READ_ONLY:
99             access_mode = OpenFile::AccessMode::kReadOnly;
100             break;
101           case dittosuiteproto::AccessMode::WRITE_ONLY:
102             access_mode = OpenFile::AccessMode::kWriteOnly;
103             break;
104           case dittosuiteproto::AccessMode::READ_WRITE:
105             access_mode = OpenFile::AccessMode::kReadWrite;
106             break;
107           default:
108             LOGF("Invalid instruction OpenFile access mode: it should be at least read or write");
109             break;
110         }
111       }
112 
113       if (options.has_input()) {
114         int input_key = SharedVariables::GetKey(thread_ids, options.input());
115         return std::make_unique<OpenFile>(instruction_params, input_key, options.create(),
116                                           options.direct_io(), fd_key, access_mode);
117       } else if (options.has_path_name()) {
118         return std::make_unique<OpenFile>(instruction_params, options.path_name(), options.create(),
119                                           options.direct_io(), fd_key, access_mode);
120       } else {
121         return std::make_unique<OpenFile>(instruction_params, options.create(), options.direct_io(),
122                                           fd_key, access_mode);
123       }
124     }
125     case InstructionType::kDeleteFile: {
126       const auto& options = proto_instruction.delete_file();
127 
128       if (options.has_input()) {
129         int input_key = SharedVariables::GetKey(thread_ids, options.input());
130         return std::make_unique<DeleteFile>(instruction_params, input_key);
131       } else {
132         return std::make_unique<DeleteFile>(instruction_params, options.path_name());
133       }
134     }
135     case InstructionType::kCloseFile: {
136       const auto& options = proto_instruction.close_file();
137 
138       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
139 
140       return std::make_unique<CloseFile>(instruction_params, fd_key);
141     }
142     case InstructionType::kResizeFile: {
143       const auto& options = proto_instruction.resize_file();
144 
145       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
146 
147       return std::make_unique<ResizeFile>(instruction_params, options.size(), fd_key);
148     }
149     case InstructionType::kWriteFile: {
150       const auto& options = proto_instruction.write_file();
151 
152       auto access_order = ConvertOrder(options.access_order());
153 
154       uint32_t seed = options.seed();
155       if (!options.has_seed()) {
156         seed = time(nullptr);
157       }
158 
159       auto reseeding = ConvertReseeding(options.reseeding());
160       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
161 
162       return std::make_unique<WriteFile>(instruction_params, options.size(), options.block_size(),
163                                          options.starting_offset(), access_order, seed, reseeding,
164                                          options.fsync(), fd_key);
165     }
166     case InstructionType::kReadFile: {
167       const auto& options = proto_instruction.read_file();
168 
169       auto access_order = ConvertOrder(options.access_order());
170 
171       uint32_t seed = options.seed();
172       if (!options.has_seed()) {
173         seed = time(nullptr);
174       }
175 
176       auto fadvise = ConvertReadFAdvise(access_order, options.fadvise());
177       auto reseeding = ConvertReseeding(options.reseeding());
178       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
179 
180       return std::make_unique<ReadFile>(instruction_params, options.size(), options.block_size(),
181                                         options.starting_offset(), access_order, seed, reseeding,
182                                         fadvise, fd_key);
183     }
184     case InstructionType::kReadDirectory: {
185       const auto& options = proto_instruction.read_directory();
186 
187       int output_key = SharedVariables::GetKey(thread_ids, options.output());
188 
189       return std::make_unique<ReadDirectory>(instruction_params, options.directory_name(),
190                                              output_key);
191     }
192     case InstructionType::kResizeFileRandom: {
193       const auto& options = proto_instruction.resize_file_random();
194 
195       uint32_t seed = options.seed();
196       if (!options.has_seed()) {
197         seed = time(nullptr);
198       }
199 
200       auto reseeding = ConvertReseeding(options.reseeding());
201       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
202 
203       return std::make_unique<ResizeFileRandom>(instruction_params, options.min(), options.max(),
204                                                 seed, reseeding, fd_key);
205     }
206     case InstructionType::kMultithreading: {
207       const auto& options = proto_instruction.multithreading();
208 
209       std::vector<MultithreadingParams> thread_params;
210       std::vector<std::unique_ptr<Instruction>> instructions;
211 
212       for (int t = 0; t < options.threads().size(); t++) {
213         const auto& thread = options.threads()[t];
214         for (int i = 0; i < thread.spawn(); i++) {
215           auto thread_ids_copy = thread_ids;
216           thread_ids_copy.push_back(InstructionFactory::GenerateThreadId());
217           instructions.push_back(InstructionFactory::CreateFromProtoInstruction(
218               thread_ids_copy, thread.instruction()));
219 
220           std::string thread_name;
221           if (thread.has_name()) {
222             thread_name = thread.name() + "_" + std::to_string(i);
223           } else {
224             thread_name = std::to_string(t) + "_" + std::to_string(i);
225           }
226 
227           SchedAttr sched_attr(Syscall::GetSyscall());
228           if (thread.has_sched_attr()) {
229             sched_attr = thread.sched_attr();
230           }
231 
232           SchedAffinity sched_affinity(Syscall::GetSyscall());
233           if (thread.has_sched_affinity()) {
234             sched_affinity = thread.sched_affinity();
235           }
236 
237           thread_params.push_back(MultithreadingParams(thread_name, sched_attr, sched_affinity));
238         }
239       }
240 
241       if (options.fork()) {
242         return std::make_unique<Multiprocessing>(instruction_params, std::move(instructions),
243                                                  std::move(thread_params));
244       } else {
245         return std::make_unique<Multithreading>(instruction_params, std::move(instructions),
246                                                 std::move(thread_params));
247       }
248     }
249     case InstructionType::kInvalidateCache: {
250       return std::make_unique<InvalidateCache>(instruction_params);
251     }
252 #if __ANDROID__
253     case InstructionType::kBinderRequest: {
254       const auto& binder_request = proto_instruction.binder_request();
255       switch (binder_request.service_oneof_case()) {
256         case RequestService::kServiceName: {
257           const auto& options = proto_instruction.binder_request();
258           return std::make_unique<BinderRequestDitto>(instruction_params, options.service_name());
259           break;
260         }
261         case RequestService::kRunningService: {
262           return std::make_unique<BinderRequestMountService>(instruction_params);
263           break;
264         }
265         case RequestService::kGenericService: {
266           const auto& options = proto_instruction.binder_request();
267           const auto& generic_service = options.generic_service();
268           return std::make_unique<GenericBinderRequest>(instruction_params, generic_service.name(), generic_service.code(), generic_service.parcel_input());
269         }
270         case RequestService::SERVICE_ONEOF_NOT_SET: {
271           LOGF("No service specified for BinderRequest");
272           break;
273         }
274       }
275       break;
276     }
277     case InstructionType::kBinderService: {
278       const auto& options = proto_instruction.binder_service();
279 
280       return std::make_unique<BinderService>(instruction_params, options.name(), options.threads());
281     }
282 #endif /*__ANDROID__*/
283     case InstructionType::kCpuWork: {
284       const auto& options = proto_instruction.cpu_work();
285 
286       switch (options.type_case()) {
287         case CpuWorkType::kCycles: {
288           return std::make_unique<CpuWorkCycles>(instruction_params, options.cycles());
289           break;
290         }
291         case CpuWorkType::kUtilization: {
292           return std::make_unique<CpuWorkUtilization>(instruction_params, options.utilization());
293           break;
294         }
295         case CpuWorkType::kDurationUs: {
296           return std::make_unique<CpuWorkDurationUs>(instruction_params, options.duration_us());
297           break;
298         }
299         case CpuWorkType::TYPE_NOT_SET: {
300           LOGF("No type specified for CpuWorkload");
301           break;
302         }
303       }
304     }
305     case InstructionType::kMemAlloc: {
306       const auto& options = proto_instruction.mem_alloc();
307 
308       dittosuite::FreePolicy free_policy = ConvertFreePolicy(options.free_policy());
309       return std::make_unique<MemoryAllocation>(instruction_params, options.size(), free_policy);
310       break;
311     }
312     case InstructionType::kLock: {
313       const auto& options = proto_instruction.lock();
314 
315       if (!options.has_mutex() || !options.mutex().has_name()) {
316         LOGF("Locking instruction must have a mutex and the mutex must be named");
317       }
318       if (!SharedVariables::Exists(thread_ids, options.mutex().name())) {
319         LOGF(
320             "Could not find mutex declaration. Mutexes must be declared in the global section of "
321             "the .ditto file");
322       }
323 
324       auto mux_key = SharedVariables::GetKey(thread_ids, options.mutex().name());
325       auto mux = std::get_if<pthread_mutex_t>(SharedVariables::GetPointer(mux_key));
326 
327       return std::make_unique<Lock>(instruction_params, mux);
328       break;
329     }
330     case InstructionType::kUnlock: {
331       const auto& options = proto_instruction.unlock();
332 
333       if (!options.has_mutex() || !options.mutex().has_name()) {
334         LOGF("Locking instruction must have a mutex and the mutex must be named");
335       }
336 
337       if (!SharedVariables::Exists(thread_ids, options.mutex().name())) {
338         LOGF(
339             "Could not find mutex declaration. Mutexes must be declared in the global section of "
340             "the .ditto file");
341       }
342 
343       auto mux_key = SharedVariables::GetKey(thread_ids, options.mutex().name());
344       auto mux = std::get_if<pthread_mutex_t>(SharedVariables::GetPointer(mux_key));
345 
346       return std::make_unique<Unlock>(instruction_params, mux);
347       break;
348     }
349     case InstructionType::INSTRUCTION_ONEOF_NOT_SET: {
350       LOGF("Instruction was not set in .ditto file");
351       break;
352     }
353     default: {
354       LOGF("Invalid instruction was set in .ditto file");
355     }
356   }
357 }
358 
GenerateThreadId()359 int InstructionFactory::GenerateThreadId() {
360   return current_thread_id_++;
361 }
362 
363 int InstructionFactory::current_thread_id_ = 0;
364 
ConvertReseeding(const dittosuiteproto::Reseeding proto_reseeding)365 Reseeding InstructionFactory::ConvertReseeding(const dittosuiteproto::Reseeding proto_reseeding) {
366   switch (proto_reseeding) {
367     case dittosuiteproto::Reseeding::ONCE: {
368       return Reseeding::kOnce;
369     }
370     case dittosuiteproto::Reseeding::EACH_ROUND_OF_CYCLES: {
371       return Reseeding::kEachRoundOfCycles;
372     }
373     case dittosuiteproto::Reseeding::EACH_CYCLE: {
374       return Reseeding::kEachCycle;
375     }
376     default: {
377       LOGF("Invalid Reseeding was provided");
378     }
379   }
380 }
381 
ConvertOrder(const dittosuiteproto::Order proto_order)382 Order InstructionFactory::ConvertOrder(const dittosuiteproto::Order proto_order) {
383   switch (proto_order) {
384     case dittosuiteproto::Order::SEQUENTIAL: {
385       return Order::kSequential;
386     }
387     case dittosuiteproto::Order::RANDOM: {
388       return Order::kRandom;
389     }
390     default: {
391       LOGF("Invalid Order was provided");
392     }
393   }
394 }
395 
ConvertReadFAdvise(const Order access_order,const dittosuiteproto::ReadFile_ReadFAdvise proto_fadvise)396 int InstructionFactory::ConvertReadFAdvise(
397     const Order access_order, const dittosuiteproto::ReadFile_ReadFAdvise proto_fadvise) {
398   switch (proto_fadvise) {
399     case dittosuiteproto::ReadFile_ReadFAdvise_AUTOMATIC: {
400       switch (access_order) {
401         case Order::kSequential: {
402           return POSIX_FADV_SEQUENTIAL;
403         }
404         case Order::kRandom: {
405           return POSIX_FADV_RANDOM;
406         }
407       }
408     }
409     case dittosuiteproto::ReadFile_ReadFAdvise_NORMAL: {
410       return POSIX_FADV_NORMAL;
411     }
412     case dittosuiteproto::ReadFile_ReadFAdvise_SEQUENTIAL: {
413       return POSIX_FADV_SEQUENTIAL;
414     }
415     case dittosuiteproto::ReadFile_ReadFAdvise_RANDOM: {
416       return POSIX_FADV_RANDOM;
417     }
418     default: {
419       LOGF("Invalid ReadFAdvise was provided");
420     }
421   }
422 }
423 
ConvertFreePolicy(const dittosuiteproto::FreePolicy proto_policy)424 FreePolicy InstructionFactory::ConvertFreePolicy(const dittosuiteproto::FreePolicy proto_policy) {
425   switch (proto_policy) {
426     case dittosuiteproto::FreePolicy::FREE_POLICY_EVERY_PERIOD:
427       return FreePolicy::kFreeEveryPeriod;
428     case dittosuiteproto::FreePolicy::FREE_POLICY_LAST_PERIOD:
429       return FreePolicy::kFreeLastPeriod;
430     case dittosuiteproto::FreePolicy::FREE_POLICY_KEEP:
431       return FreePolicy::kKeep;
432     default: {
433       LOGF("Invalid FreePolicy");
434     }
435   }
436 }
437 
438 }  // namespace dittosuite
439