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/logger.h>
30 #include <ditto/memory_allocation.h>
31 #include <ditto/multiprocessing.h>
32 #include <ditto/multithreading.h>
33 #include <ditto/multithreading_utils.h>
34 #include <ditto/open_file.h>
35 #include <ditto/read_directory.h>
36 #include <ditto/read_write_file.h>
37 #include <ditto/resize_file.h>
38 #include <ditto/shared_variables.h>
39 #include <ditto/syscall.h>
40 
41 namespace dittosuite {
42 typedef dittosuiteproto::Instruction::InstructionOneofCase InstructionType;
43 typedef dittosuiteproto::BinderRequest::ServiceOneofCase RequestService;
44 typedef dittosuiteproto::CpuWork::TypeCase CpuWorkType;
45 
CreateFromProtoInstructionSet(const dittosuite::Instruction::Params & instruction_params,const std::list<int> & thread_ids,const dittosuiteproto::InstructionSet & proto_instruction_set)46 std::unique_ptr<InstructionSet> InstructionFactory::CreateFromProtoInstructionSet(
47     const dittosuite::Instruction::Params& instruction_params, const std::list<int>& thread_ids,
48     const dittosuiteproto::InstructionSet& proto_instruction_set) {
49   std::vector<std::unique_ptr<Instruction>> instructions;
50   for (const auto& instruction : proto_instruction_set.instructions()) {
51     instructions.push_back(
52         std::move(InstructionFactory::CreateFromProtoInstruction(thread_ids, instruction)));
53   }
54 
55   if (proto_instruction_set.has_iterate_options()) {
56     const auto& options = proto_instruction_set.iterate_options();
57 
58     int list_key = SharedVariables::GetKey(thread_ids, options.list_name());
59     int item_key = SharedVariables::GetKey(thread_ids, options.item_name());
60     auto access_order = ConvertOrder(options.access_order());
61     auto reseeding = ConvertReseeding(options.reseeding());
62 
63     uint32_t seed = options.seed();
64     if (!options.has_seed()) {
65       seed = time(nullptr);
66     }
67 
68     return std::make_unique<InstructionSet>(instruction_params, std::move(instructions), list_key,
69                                             item_key, access_order, reseeding, seed);
70   } else {
71     return std::make_unique<InstructionSet>(instruction_params, std::move(instructions));
72   }
73 }
74 
CreateFromProtoInstruction(const std::list<int> & thread_ids,const dittosuiteproto::Instruction & proto_instruction)75 std::unique_ptr<Instruction> InstructionFactory::CreateFromProtoInstruction(
76     const std::list<int>& thread_ids, const dittosuiteproto::Instruction& proto_instruction) {
77   Instruction::Params instruction_params(Syscall::GetSyscall(), proto_instruction.repeat(),
78                                          proto_instruction.period_us(),
79                                          proto_instruction.offset_us());
80 
81   switch (proto_instruction.instruction_oneof_case()) {
82     case InstructionType::kInstructionSet: {
83       return InstructionFactory::CreateFromProtoInstructionSet(instruction_params, thread_ids,
84                                                                proto_instruction.instruction_set());
85     }
86     case InstructionType::kOpenFile: {
87       const auto& options = proto_instruction.open_file();
88 
89       int fd_key = -1;
90       if (options.has_output_fd()) {
91         fd_key = SharedVariables::GetKey(thread_ids, options.output_fd());
92       }
93 
94       dittosuite::OpenFile::AccessMode access_mode;
95       {
96         switch (options.access_mode()) {
97           case dittosuiteproto::AccessMode::READ_ONLY:
98             access_mode = OpenFile::AccessMode::kReadOnly;
99             break;
100           case dittosuiteproto::AccessMode::WRITE_ONLY:
101             access_mode = OpenFile::AccessMode::kWriteOnly;
102             break;
103           case dittosuiteproto::AccessMode::READ_WRITE:
104             access_mode = OpenFile::AccessMode::kReadWrite;
105             break;
106           default:
107             LOGF("Invalid instruction OpenFile access mode: it should be at least read or write");
108             break;
109         }
110       }
111 
112       if (options.has_input()) {
113         int input_key = SharedVariables::GetKey(thread_ids, options.input());
114         return std::make_unique<OpenFile>(instruction_params, input_key, options.create(),
115                                           options.direct_io(), fd_key, access_mode);
116       } else if (options.has_path_name()) {
117         return std::make_unique<OpenFile>(instruction_params, options.path_name(), options.create(),
118                                           options.direct_io(), fd_key, access_mode);
119       } else {
120         return std::make_unique<OpenFile>(instruction_params, options.create(), options.direct_io(),
121                                           fd_key, access_mode);
122       }
123     }
124     case InstructionType::kDeleteFile: {
125       const auto& options = proto_instruction.delete_file();
126 
127       if (options.has_input()) {
128         int input_key = SharedVariables::GetKey(thread_ids, options.input());
129         return std::make_unique<DeleteFile>(instruction_params, input_key);
130       } else {
131         return std::make_unique<DeleteFile>(instruction_params, options.path_name());
132       }
133     }
134     case InstructionType::kCloseFile: {
135       const auto& options = proto_instruction.close_file();
136 
137       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
138 
139       return std::make_unique<CloseFile>(instruction_params, fd_key);
140     }
141     case InstructionType::kResizeFile: {
142       const auto& options = proto_instruction.resize_file();
143 
144       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
145 
146       return std::make_unique<ResizeFile>(instruction_params, options.size(), fd_key);
147     }
148     case InstructionType::kWriteFile: {
149       const auto& options = proto_instruction.write_file();
150 
151       auto access_order = ConvertOrder(options.access_order());
152 
153       uint32_t seed = options.seed();
154       if (!options.has_seed()) {
155         seed = time(nullptr);
156       }
157 
158       auto reseeding = ConvertReseeding(options.reseeding());
159       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
160 
161       return std::make_unique<WriteFile>(instruction_params, options.size(), options.block_size(),
162                                          options.starting_offset(), access_order, seed, reseeding,
163                                          options.fsync(), fd_key);
164     }
165     case InstructionType::kReadFile: {
166       const auto& options = proto_instruction.read_file();
167 
168       auto access_order = ConvertOrder(options.access_order());
169 
170       uint32_t seed = options.seed();
171       if (!options.has_seed()) {
172         seed = time(nullptr);
173       }
174 
175       auto fadvise = ConvertReadFAdvise(access_order, options.fadvise());
176       auto reseeding = ConvertReseeding(options.reseeding());
177       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
178 
179       return std::make_unique<ReadFile>(instruction_params, options.size(), options.block_size(),
180                                         options.starting_offset(), access_order, seed, reseeding,
181                                         fadvise, fd_key);
182     }
183     case InstructionType::kReadDirectory: {
184       const auto& options = proto_instruction.read_directory();
185 
186       int output_key = SharedVariables::GetKey(thread_ids, options.output());
187 
188       return std::make_unique<ReadDirectory>(instruction_params, options.directory_name(),
189                                              output_key);
190     }
191     case InstructionType::kResizeFileRandom: {
192       const auto& options = proto_instruction.resize_file_random();
193 
194       uint32_t seed = options.seed();
195       if (!options.has_seed()) {
196         seed = time(nullptr);
197       }
198 
199       auto reseeding = ConvertReseeding(options.reseeding());
200       int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
201 
202       return std::make_unique<ResizeFileRandom>(instruction_params, options.min(), options.max(),
203                                                 seed, reseeding, fd_key);
204     }
205     case InstructionType::kMultithreading: {
206       const auto& options = proto_instruction.multithreading();
207 
208       std::vector<MultithreadingParams> thread_params;
209       std::vector<std::unique_ptr<Instruction>> instructions;
210 
211       for (int t = 0; t < options.threads().size(); t++) {
212         const auto& thread = options.threads()[t];
213         for (int i = 0; i < thread.spawn(); i++) {
214           auto thread_ids_copy = thread_ids;
215           thread_ids_copy.push_back(InstructionFactory::GenerateThreadId());
216           instructions.push_back(std::move(InstructionFactory::CreateFromProtoInstruction(
217               thread_ids_copy, thread.instruction())));
218 
219           std::string thread_name;
220           if (thread.has_name()) {
221             thread_name = thread.name() + "_" + std::to_string(i);
222           } else {
223             thread_name = std::to_string(t) + "_" + std::to_string(i);
224           }
225 
226           SchedAttr sched_attr(Syscall::GetSyscall());
227           if (thread.has_sched_attr()) {
228             sched_attr = thread.sched_attr();
229           }
230 
231           SchedAffinity sched_affinity(Syscall::GetSyscall());
232           if (thread.has_sched_affinity()) {
233             sched_affinity = thread.sched_affinity();
234           }
235 
236           thread_params.push_back(MultithreadingParams(thread_name, sched_attr, sched_affinity));
237         }
238       }
239 
240       if (options.fork()) {
241         return std::make_unique<Multiprocessing>(instruction_params, std::move(instructions),
242                                                  std::move(thread_params));
243       } else {
244         return std::make_unique<Multithreading>(instruction_params, std::move(instructions),
245                                                 std::move(thread_params));
246       }
247     }
248     case InstructionType::kInvalidateCache: {
249       return std::make_unique<InvalidateCache>(instruction_params);
250     }
251 #if __ANDROID__
252     case InstructionType::kBinderRequest: {
253       const auto& binder_request = proto_instruction.binder_request();
254       switch (binder_request.service_oneof_case()) {
255         case RequestService::kServiceName: {
256           const auto& options = proto_instruction.binder_request();
257           return std::make_unique<BinderRequestDitto>(instruction_params, options.service_name());
258           break;
259         }
260         case RequestService::kRunningService: {
261           return std::make_unique<BinderRequestMountService>(instruction_params);
262           break;
263         }
264         case RequestService::kGenericService: {
265           const auto& options = proto_instruction.binder_request();
266           const auto& generic_service = options.generic_service();
267           return std::make_unique<GenericBinderRequest>(instruction_params, generic_service.name(), generic_service.code(), generic_service.parcel_input());
268         }
269         case RequestService::SERVICE_ONEOF_NOT_SET: {
270           LOGF("No service specified for BinderRequest");
271           break;
272         }
273       }
274       break;
275     }
276     case InstructionType::kBinderService: {
277       const auto& options = proto_instruction.binder_service();
278 
279       return std::make_unique<BinderService>(instruction_params, options.name(), options.threads());
280     }
281 #endif /*__ANDROID__*/
282     case InstructionType::kCpuWork: {
283       const auto& options = proto_instruction.cpu_work();
284 
285       switch (options.type_case()) {
286         case CpuWorkType::kCycles: {
287           return std::make_unique<CpuWorkCycles>(instruction_params, options.cycles());
288           break;
289         }
290         case CpuWorkType::kUtilization: {
291           return std::make_unique<CpuWorkUtilization>(instruction_params, options.utilization());
292           break;
293         }
294         case CpuWorkType::TYPE_NOT_SET: {
295           LOGF("No type specified for CpuWorkload");
296           break;
297         }
298       }
299     }
300     case InstructionType::kMemAlloc: {
301       const auto& options = proto_instruction.mem_alloc();
302 
303       dittosuite::FreePolicy free_policy = ConvertFreePolicy(options.free_policy());
304       return std::make_unique<MemoryAllocation>(instruction_params, options.size(), free_policy);
305       break;
306     }
307     case InstructionType::INSTRUCTION_ONEOF_NOT_SET: {
308       LOGF("Instruction was not set in .ditto file");
309       break;
310     }
311     default: {
312       LOGF("Invalid instruction was set in .ditto file");
313     }
314   }
315 }
316 
GenerateThreadId()317 int InstructionFactory::GenerateThreadId() {
318   return current_thread_id_++;
319 }
320 
321 int InstructionFactory::current_thread_id_ = 0;
322 
ConvertReseeding(const dittosuiteproto::Reseeding proto_reseeding)323 Reseeding InstructionFactory::ConvertReseeding(const dittosuiteproto::Reseeding proto_reseeding) {
324   switch (proto_reseeding) {
325     case dittosuiteproto::Reseeding::ONCE: {
326       return Reseeding::kOnce;
327     }
328     case dittosuiteproto::Reseeding::EACH_ROUND_OF_CYCLES: {
329       return Reseeding::kEachRoundOfCycles;
330     }
331     case dittosuiteproto::Reseeding::EACH_CYCLE: {
332       return Reseeding::kEachCycle;
333     }
334     default: {
335       LOGF("Invalid Reseeding was provided");
336     }
337   }
338 }
339 
ConvertOrder(const dittosuiteproto::Order proto_order)340 Order InstructionFactory::ConvertOrder(const dittosuiteproto::Order proto_order) {
341   switch (proto_order) {
342     case dittosuiteproto::Order::SEQUENTIAL: {
343       return Order::kSequential;
344     }
345     case dittosuiteproto::Order::RANDOM: {
346       return Order::kRandom;
347     }
348     default: {
349       LOGF("Invalid Order was provided");
350     }
351   }
352 }
353 
ConvertReadFAdvise(const Order access_order,const dittosuiteproto::ReadFile_ReadFAdvise proto_fadvise)354 int InstructionFactory::ConvertReadFAdvise(
355     const Order access_order, const dittosuiteproto::ReadFile_ReadFAdvise proto_fadvise) {
356   switch (proto_fadvise) {
357     case dittosuiteproto::ReadFile_ReadFAdvise_AUTOMATIC: {
358       switch (access_order) {
359         case Order::kSequential: {
360           return POSIX_FADV_SEQUENTIAL;
361         }
362         case Order::kRandom: {
363           return POSIX_FADV_RANDOM;
364         }
365       }
366     }
367     case dittosuiteproto::ReadFile_ReadFAdvise_NORMAL: {
368       return POSIX_FADV_NORMAL;
369     }
370     case dittosuiteproto::ReadFile_ReadFAdvise_SEQUENTIAL: {
371       return POSIX_FADV_SEQUENTIAL;
372     }
373     case dittosuiteproto::ReadFile_ReadFAdvise_RANDOM: {
374       return POSIX_FADV_RANDOM;
375     }
376     default: {
377       LOGF("Invalid ReadFAdvise was provided");
378     }
379   }
380 }
381 
ConvertFreePolicy(const dittosuiteproto::FreePolicy proto_policy)382 FreePolicy InstructionFactory::ConvertFreePolicy(const dittosuiteproto::FreePolicy proto_policy) {
383   switch (proto_policy) {
384     case dittosuiteproto::FreePolicy::FREE_POLICY_EVERY_PERIOD:
385       return FreePolicy::kFreeEveryPeriod;
386     case dittosuiteproto::FreePolicy::FREE_POLICY_LAST_PERIOD:
387       return FreePolicy::kFreeLastPeriod;
388     case dittosuiteproto::FreePolicy::FREE_POLICY_KEEP:
389       return FreePolicy::kKeep;
390     default: {
391       LOGF("Invalid FreePolicy");
392     }
393   }
394 }
395 
396 }  // namespace dittosuite
397