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