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