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/close_file.h>
23 #include <ditto/delete_file.h>
24 #include <ditto/instruction_set.h>
25 #include <ditto/invalidate_cache.h>
26 #include <ditto/logger.h>
27 #include <ditto/multithreading.h>
28 #include <ditto/open_file.h>
29 #include <ditto/read_directory.h>
30 #include <ditto/read_write_file.h>
31 #include <ditto/resize_file.h>
32 #include <ditto/shared_variables.h>
33 #include <ditto/syscall.h>
34
35 namespace dittosuite {
36 typedef dittosuiteproto::Instruction::InstructionOneofCase InstructionType;
37
CreateFromProtoInstructionSet(const std::list<int> & thread_ids,const int repeat,const dittosuiteproto::InstructionSet & proto_instruction_set)38 std::unique_ptr<InstructionSet> InstructionFactory::CreateFromProtoInstructionSet(
39 const std::list<int>& thread_ids, const int repeat,
40 const dittosuiteproto::InstructionSet& proto_instruction_set) {
41 std::vector<std::unique_ptr<Instruction>> instructions;
42 for (const auto& instruction : proto_instruction_set.instructions()) {
43 instructions.push_back(
44 std::move(InstructionFactory::CreateFromProtoInstruction(thread_ids, instruction)));
45 }
46
47 if (proto_instruction_set.has_iterate_options()) {
48 const auto& options = proto_instruction_set.iterate_options();
49
50 int list_key = SharedVariables::GetKey(thread_ids, options.list_name());
51 int item_key = SharedVariables::GetKey(thread_ids, options.item_name());
52 auto access_order = ConvertOrder(options.access_order());
53 auto reseeding = ConvertReseeding(options.reseeding());
54
55 uint32_t seed = options.seed();
56 if (!options.has_seed()) {
57 seed = time(nullptr);
58 }
59
60 return std::make_unique<InstructionSet>(Syscall::GetSyscall(), repeat, std::move(instructions),
61 list_key, item_key, access_order, reseeding, seed);
62 } else {
63 return std::make_unique<InstructionSet>(Syscall::GetSyscall(), repeat, std::move(instructions));
64 }
65 }
66
CreateFromProtoInstruction(const std::list<int> & thread_ids,const dittosuiteproto::Instruction & proto_instruction)67 std::unique_ptr<Instruction> InstructionFactory::CreateFromProtoInstruction(
68 const std::list<int>& thread_ids, const dittosuiteproto::Instruction& proto_instruction) {
69 int repeat = proto_instruction.repeat();
70
71 switch (proto_instruction.instruction_oneof_case()) {
72 case InstructionType::kInstructionSet: {
73 return InstructionFactory::CreateFromProtoInstructionSet(thread_ids, repeat,
74 proto_instruction.instruction_set());
75 }
76 case InstructionType::kOpenFile: {
77 const auto& options = proto_instruction.open_file();
78
79 int fd_key = -1;
80 if (options.has_output_fd()) {
81 fd_key = SharedVariables::GetKey(thread_ids, options.output_fd());
82 }
83
84 dittosuite::OpenFile::AccessMode access_mode;
85 {
86 switch (options.access_mode()) {
87 case dittosuiteproto::AccessMode::READ_ONLY:
88 access_mode = OpenFile::AccessMode::kReadOnly;
89 break;
90 case dittosuiteproto::AccessMode::WRITE_ONLY:
91 access_mode = OpenFile::AccessMode::kWriteOnly;
92 break;
93 case dittosuiteproto::AccessMode::READ_WRITE:
94 access_mode = OpenFile::AccessMode::kReadWrite;
95 break;
96 default:
97 LOGF("Invalid instruction OpenFile access mode: it should be at least read or write");
98 break;
99 }
100 }
101
102 if (options.has_input()) {
103 int input_key = SharedVariables::GetKey(thread_ids, options.input());
104 return std::make_unique<OpenFile>(Syscall::GetSyscall(), repeat, input_key,
105 options.create(), options.direct_io(), fd_key,
106 access_mode);
107 } else if (options.has_path_name()) {
108 return std::make_unique<OpenFile>(Syscall::GetSyscall(), repeat, options.path_name(),
109 options.create(), options.direct_io(), fd_key,
110 access_mode);
111 } else {
112 return std::make_unique<OpenFile>(Syscall::GetSyscall(), repeat, options.create(),
113 options.direct_io(), fd_key, access_mode);
114 }
115 }
116 case InstructionType::kDeleteFile: {
117 const auto& options = proto_instruction.delete_file();
118
119 if (options.has_input()) {
120 int input_key = SharedVariables::GetKey(thread_ids, options.input());
121 return std::make_unique<DeleteFile>(Syscall::GetSyscall(), repeat, input_key);
122 } else {
123 return std::make_unique<DeleteFile>(Syscall::GetSyscall(), repeat, options.path_name());
124 }
125 }
126 case InstructionType::kCloseFile: {
127 const auto& options = proto_instruction.close_file();
128
129 int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
130
131 return std::make_unique<CloseFile>(Syscall::GetSyscall(), repeat, fd_key);
132 }
133 case InstructionType::kResizeFile: {
134 const auto& options = proto_instruction.resize_file();
135
136 int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
137
138 return std::make_unique<ResizeFile>(Syscall::GetSyscall(), repeat, options.size(), fd_key);
139 }
140 case InstructionType::kWriteFile: {
141 const auto& options = proto_instruction.write_file();
142
143 auto access_order = ConvertOrder(options.access_order());
144
145 uint32_t seed = options.seed();
146 if (!options.has_seed()) {
147 seed = time(nullptr);
148 }
149
150 auto reseeding = ConvertReseeding(options.reseeding());
151 int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
152
153 return std::make_unique<WriteFile>(Syscall::GetSyscall(), repeat, options.size(),
154 options.block_size(), options.starting_offset(),
155 access_order, seed, reseeding, options.fsync(), fd_key);
156 }
157 case InstructionType::kReadFile: {
158 const auto& options = proto_instruction.read_file();
159
160 auto access_order = ConvertOrder(options.access_order());
161
162 uint32_t seed = options.seed();
163 if (!options.has_seed()) {
164 seed = time(nullptr);
165 }
166
167 auto fadvise = ConvertReadFAdvise(access_order, options.fadvise());
168 auto reseeding = ConvertReseeding(options.reseeding());
169 int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
170
171 return std::make_unique<ReadFile>(Syscall::GetSyscall(), repeat, options.size(),
172 options.block_size(), options.starting_offset(),
173 access_order, seed, reseeding, fadvise, fd_key);
174 }
175 case InstructionType::kReadDirectory: {
176 const auto& options = proto_instruction.read_directory();
177
178 int output_key = SharedVariables::GetKey(thread_ids, options.output());
179
180 return std::make_unique<ReadDirectory>(Syscall::GetSyscall(), repeat,
181 options.directory_name(), output_key);
182 }
183 case InstructionType::kResizeFileRandom: {
184 const auto& options = proto_instruction.resize_file_random();
185
186 uint32_t seed = options.seed();
187 if (!options.has_seed()) {
188 seed = time(nullptr);
189 }
190
191 auto reseeding = ConvertReseeding(options.reseeding());
192 int fd_key = SharedVariables::GetKey(thread_ids, options.input_fd());
193
194 return std::make_unique<ResizeFileRandom>(Syscall::GetSyscall(), repeat, options.min(),
195 options.max(), seed, reseeding, fd_key);
196 }
197 case InstructionType::kMultithreading: {
198 const auto& options = proto_instruction.multithreading();
199
200 std::vector<std::unique_ptr<Instruction>> instructions;
201 for (const auto& thread : options.threads()) {
202 for (int i = 0; i < thread.spawn(); i++) {
203 auto thread_ids_copy = thread_ids;
204 thread_ids_copy.push_back(InstructionFactory::GenerateThreadId());
205 instructions.push_back(std::move(InstructionFactory::CreateFromProtoInstruction(
206 thread_ids_copy, thread.instruction())));
207 }
208 }
209
210 return std::make_unique<Multithreading>(Syscall::GetSyscall(), repeat,
211 std::move(instructions));
212 }
213 case InstructionType::kInvalidateCache: {
214 return std::make_unique<InvalidateCache>(Syscall::GetSyscall(), repeat);
215 }
216 case InstructionType::INSTRUCTION_ONEOF_NOT_SET: {
217 LOGF("Instruction was not set in .ditto file");
218 }
219 default: {
220 LOGF("Invalid instruction was set in .ditto file");
221 }
222 }
223 }
224
GenerateThreadId()225 int InstructionFactory::GenerateThreadId() {
226 return current_thread_id_++;
227 }
228
229 int InstructionFactory::current_thread_id_ = 0;
230
ConvertReseeding(const dittosuiteproto::Reseeding proto_reseeding)231 Reseeding InstructionFactory::ConvertReseeding(const dittosuiteproto::Reseeding proto_reseeding) {
232 switch (proto_reseeding) {
233 case dittosuiteproto::Reseeding::ONCE: {
234 return Reseeding::kOnce;
235 }
236 case dittosuiteproto::Reseeding::EACH_ROUND_OF_CYCLES: {
237 return Reseeding::kEachRoundOfCycles;
238 }
239 case dittosuiteproto::Reseeding::EACH_CYCLE: {
240 return Reseeding::kEachCycle;
241 }
242 default: {
243 LOGF("Invalid Reseeding was provided");
244 }
245 }
246 }
247
ConvertOrder(const dittosuiteproto::Order proto_order)248 Order InstructionFactory::ConvertOrder(const dittosuiteproto::Order proto_order) {
249 switch (proto_order) {
250 case dittosuiteproto::Order::SEQUENTIAL: {
251 return Order::kSequential;
252 }
253 case dittosuiteproto::Order::RANDOM: {
254 return Order::kRandom;
255 }
256 default: {
257 LOGF("Invalid Order was provided");
258 }
259 }
260 }
261
ConvertReadFAdvise(const Order access_order,const dittosuiteproto::ReadFile_ReadFAdvise proto_fadvise)262 int InstructionFactory::ConvertReadFAdvise(
263 const Order access_order, const dittosuiteproto::ReadFile_ReadFAdvise proto_fadvise) {
264 switch (proto_fadvise) {
265 case dittosuiteproto::ReadFile_ReadFAdvise_AUTOMATIC: {
266 switch (access_order) {
267 case Order::kSequential: {
268 return POSIX_FADV_SEQUENTIAL;
269 }
270 case Order::kRandom: {
271 return POSIX_FADV_RANDOM;
272 }
273 }
274 }
275 case dittosuiteproto::ReadFile_ReadFAdvise_NORMAL: {
276 return POSIX_FADV_NORMAL;
277 }
278 case dittosuiteproto::ReadFile_ReadFAdvise_SEQUENTIAL: {
279 return POSIX_FADV_SEQUENTIAL;
280 }
281 case dittosuiteproto::ReadFile_ReadFAdvise_RANDOM: {
282 return POSIX_FADV_RANDOM;
283 }
284 default: {
285 LOGF("Invalid ReadFAdvise was provided");
286 }
287 }
288 }
289
290 } // namespace dittosuite
291