xref: /aosp_15_r20/system/vold/bench/inodeop_bench/inodeop_bench.cpp (revision f40fafd4c6c2594924d919feffc1a1fd6e3b30f3)
1*f40fafd4SAndroid Build Coastguard Worker /*
2*f40fafd4SAndroid Build Coastguard Worker  * Copyright (C) 2020 The Android Open Source Project
3*f40fafd4SAndroid Build Coastguard Worker  *
4*f40fafd4SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*f40fafd4SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*f40fafd4SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*f40fafd4SAndroid Build Coastguard Worker  *
8*f40fafd4SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*f40fafd4SAndroid Build Coastguard Worker  *
10*f40fafd4SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*f40fafd4SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*f40fafd4SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*f40fafd4SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*f40fafd4SAndroid Build Coastguard Worker  * limitations under the License.
15*f40fafd4SAndroid Build Coastguard Worker  */
16*f40fafd4SAndroid Build Coastguard Worker #include <chrono>
17*f40fafd4SAndroid Build Coastguard Worker #include <functional>
18*f40fafd4SAndroid Build Coastguard Worker #include <iostream>
19*f40fafd4SAndroid Build Coastguard Worker #include <ratio>
20*f40fafd4SAndroid Build Coastguard Worker #include <sstream>
21*f40fafd4SAndroid Build Coastguard Worker #include <string>
22*f40fafd4SAndroid Build Coastguard Worker #include <unordered_map>
23*f40fafd4SAndroid Build Coastguard Worker #include <vector>
24*f40fafd4SAndroid Build Coastguard Worker 
25*f40fafd4SAndroid Build Coastguard Worker #include <dirent.h>
26*f40fafd4SAndroid Build Coastguard Worker #include <fcntl.h>
27*f40fafd4SAndroid Build Coastguard Worker #include <stdlib.h>
28*f40fafd4SAndroid Build Coastguard Worker #include <sys/stat.h>
29*f40fafd4SAndroid Build Coastguard Worker #include <sys/types.h>
30*f40fafd4SAndroid Build Coastguard Worker #include <unistd.h>
31*f40fafd4SAndroid Build Coastguard Worker 
32*f40fafd4SAndroid Build Coastguard Worker static constexpr char VERSION[] = "0";
33*f40fafd4SAndroid Build Coastguard Worker 
34*f40fafd4SAndroid Build Coastguard Worker // Self-contained class for collecting and reporting benchmark metrics
35*f40fafd4SAndroid Build Coastguard Worker // (currently only execution time).
36*f40fafd4SAndroid Build Coastguard Worker class Collector {
37*f40fafd4SAndroid Build Coastguard Worker     using time_point = std::chrono::time_point<std::chrono::steady_clock>;
38*f40fafd4SAndroid Build Coastguard Worker     using time_unit = std::chrono::duration<double, std::milli>;
39*f40fafd4SAndroid Build Coastguard Worker 
40*f40fafd4SAndroid Build Coastguard Worker     struct Metric {
41*f40fafd4SAndroid Build Coastguard Worker         std::string workload;
42*f40fafd4SAndroid Build Coastguard Worker         time_unit exec_time;
MetricCollector::Metric43*f40fafd4SAndroid Build Coastguard Worker         Metric(const std::string& workload, const time_unit& exec_time)
44*f40fafd4SAndroid Build Coastguard Worker             : workload(workload), exec_time(exec_time) {}
45*f40fafd4SAndroid Build Coastguard Worker     };
46*f40fafd4SAndroid Build Coastguard Worker 
47*f40fafd4SAndroid Build Coastguard Worker     static constexpr char TIME_UNIT[] = "ms";
48*f40fafd4SAndroid Build Coastguard Worker     static constexpr char VERSION[] = "0";
49*f40fafd4SAndroid Build Coastguard Worker     std::vector<Metric> metrics;
50*f40fafd4SAndroid Build Coastguard Worker     time_point reset_time;
51*f40fafd4SAndroid Build Coastguard Worker 
52*f40fafd4SAndroid Build Coastguard Worker   public:
Collector()53*f40fafd4SAndroid Build Coastguard Worker     Collector() { reset(); }
54*f40fafd4SAndroid Build Coastguard Worker 
reset()55*f40fafd4SAndroid Build Coastguard Worker     void reset() { reset_time = std::chrono::steady_clock::now(); }
56*f40fafd4SAndroid Build Coastguard Worker 
collect_metric(const std::string & workload)57*f40fafd4SAndroid Build Coastguard Worker     void collect_metric(const std::string& workload) {
58*f40fafd4SAndroid Build Coastguard Worker         auto elapsed = std::chrono::steady_clock::now() - reset_time;
59*f40fafd4SAndroid Build Coastguard Worker         metrics.emplace_back(workload, std::chrono::duration_cast<time_unit>(elapsed));
60*f40fafd4SAndroid Build Coastguard Worker     }
61*f40fafd4SAndroid Build Coastguard Worker 
report_metrics()62*f40fafd4SAndroid Build Coastguard Worker     void report_metrics() {
63*f40fafd4SAndroid Build Coastguard Worker         for (const Metric& metric : metrics)
64*f40fafd4SAndroid Build Coastguard Worker             std::cout << VERSION << ";" << metric.workload << ";" << metric.exec_time.count() << ";"
65*f40fafd4SAndroid Build Coastguard Worker                       << TIME_UNIT << std::endl;
66*f40fafd4SAndroid Build Coastguard Worker     }
67*f40fafd4SAndroid Build Coastguard Worker };
68*f40fafd4SAndroid Build Coastguard Worker 
69*f40fafd4SAndroid Build Coastguard Worker struct Command {
70*f40fafd4SAndroid Build Coastguard Worker     static constexpr char CREATE[] = "create";
71*f40fafd4SAndroid Build Coastguard Worker     static constexpr char DELETE[] = "delete";
72*f40fafd4SAndroid Build Coastguard Worker     static constexpr char MOVE[] = "move";
73*f40fafd4SAndroid Build Coastguard Worker     static constexpr char HARDLINK[] = "hardlink";
74*f40fafd4SAndroid Build Coastguard Worker     static constexpr char SYMLINK[] = "symlink";
75*f40fafd4SAndroid Build Coastguard Worker     static constexpr char READDIR[] = "readdir";
76*f40fafd4SAndroid Build Coastguard Worker     std::string workload;
77*f40fafd4SAndroid Build Coastguard Worker     std::string from_dir;
78*f40fafd4SAndroid Build Coastguard Worker     std::string from_basename;
79*f40fafd4SAndroid Build Coastguard Worker     std::string to_dir;
80*f40fafd4SAndroid Build Coastguard Worker     std::string to_basename;
81*f40fafd4SAndroid Build Coastguard Worker     bool drop_state;
82*f40fafd4SAndroid Build Coastguard Worker     int n_file;
83*f40fafd4SAndroid Build Coastguard Worker 
CommandCommand84*f40fafd4SAndroid Build Coastguard Worker     Command() { reset(); }
85*f40fafd4SAndroid Build Coastguard Worker 
to_stringCommand86*f40fafd4SAndroid Build Coastguard Worker     std::string to_string() const {
87*f40fafd4SAndroid Build Coastguard Worker         std::stringstream string_repr;
88*f40fafd4SAndroid Build Coastguard Worker         string_repr << "Command {\n";
89*f40fafd4SAndroid Build Coastguard Worker         string_repr << "\t.workload = " << workload << ",\n";
90*f40fafd4SAndroid Build Coastguard Worker         string_repr << "\t.from_dir = " << from_dir << ",\n";
91*f40fafd4SAndroid Build Coastguard Worker         string_repr << "\t.from_basename = " << from_basename << ",\n";
92*f40fafd4SAndroid Build Coastguard Worker         string_repr << "\t.to_dir = " << to_dir << ",\n";
93*f40fafd4SAndroid Build Coastguard Worker         string_repr << "\t.to_basename = " << to_basename << ",\n";
94*f40fafd4SAndroid Build Coastguard Worker         string_repr << "\t.drop_state = " << drop_state << ",\n";
95*f40fafd4SAndroid Build Coastguard Worker         string_repr << "\t.n_file = " << n_file << "\n";
96*f40fafd4SAndroid Build Coastguard Worker         string_repr << "}\n";
97*f40fafd4SAndroid Build Coastguard Worker         return string_repr.str();
98*f40fafd4SAndroid Build Coastguard Worker     }
99*f40fafd4SAndroid Build Coastguard Worker 
resetCommand100*f40fafd4SAndroid Build Coastguard Worker     void reset() {
101*f40fafd4SAndroid Build Coastguard Worker         workload = "";
102*f40fafd4SAndroid Build Coastguard Worker         from_dir = to_dir = "./";
103*f40fafd4SAndroid Build Coastguard Worker         from_basename = "from_file";
104*f40fafd4SAndroid Build Coastguard Worker         to_basename = "to_file";
105*f40fafd4SAndroid Build Coastguard Worker         drop_state = true;
106*f40fafd4SAndroid Build Coastguard Worker         n_file = 0;
107*f40fafd4SAndroid Build Coastguard Worker     }
108*f40fafd4SAndroid Build Coastguard Worker };
109*f40fafd4SAndroid Build Coastguard Worker 
print_version()110*f40fafd4SAndroid Build Coastguard Worker void print_version() {
111*f40fafd4SAndroid Build Coastguard Worker     std::cout << VERSION << std::endl;
112*f40fafd4SAndroid Build Coastguard Worker }
113*f40fafd4SAndroid Build Coastguard Worker 
print_commands(const std::vector<Command> & commands)114*f40fafd4SAndroid Build Coastguard Worker void print_commands(const std::vector<Command>& commands) {
115*f40fafd4SAndroid Build Coastguard Worker     for (const Command& command : commands) std::cout << command.to_string();
116*f40fafd4SAndroid Build Coastguard Worker }
117*f40fafd4SAndroid Build Coastguard Worker 
usage(std::ostream & ostr,const std::string & program_name)118*f40fafd4SAndroid Build Coastguard Worker void usage(std::ostream& ostr, const std::string& program_name) {
119*f40fafd4SAndroid Build Coastguard Worker     Command command;
120*f40fafd4SAndroid Build Coastguard Worker 
121*f40fafd4SAndroid Build Coastguard Worker     ostr << "Usage: " << program_name << " [global_options] {[workload_options] -w WORKLOAD_T}\n";
122*f40fafd4SAndroid Build Coastguard Worker     ostr << "WORKLOAD_T = {" << Command::CREATE << ", " << Command::DELETE << ", " << Command::MOVE
123*f40fafd4SAndroid Build Coastguard Worker          << ", " << Command::HARDLINK << ", " << Command::SYMLINK << "}\n";
124*f40fafd4SAndroid Build Coastguard Worker     ostr << "Global options\n";
125*f40fafd4SAndroid Build Coastguard Worker     ostr << "\t-v: Print version.\n";
126*f40fafd4SAndroid Build Coastguard Worker     ostr << "\t-p: Print parsed workloads and exit.\n";
127*f40fafd4SAndroid Build Coastguard Worker     ostr << "Workload options\n";
128*f40fafd4SAndroid Build Coastguard Worker     ostr << "\t-d DIR\t\t: Work directory for " << Command::CREATE << "/" << Command::DELETE
129*f40fafd4SAndroid Build Coastguard Worker          << " (default '" << command.from_dir << "').\n";
130*f40fafd4SAndroid Build Coastguard Worker     ostr << "\t-f FROM-DIR\t: Source directory for " << Command::MOVE << "/" << Command::SYMLINK
131*f40fafd4SAndroid Build Coastguard Worker          << "/" << Command::HARDLINK << " (default '" << command.from_dir << "').\n";
132*f40fafd4SAndroid Build Coastguard Worker     ostr << "\t-t TO-DIR\t: Destination directory for " << Command::MOVE << "/" << Command::SYMLINK
133*f40fafd4SAndroid Build Coastguard Worker          << "/" << Command::HARDLINK << " (default '" << command.to_dir << "').\n";
134*f40fafd4SAndroid Build Coastguard Worker     ostr << "\t-n N_FILES\t: Number of files to create/delete etc. (default " << command.n_file
135*f40fafd4SAndroid Build Coastguard Worker          << ").\n";
136*f40fafd4SAndroid Build Coastguard Worker     ostr << "\t-s\t\t: Do not drop state (caches) before running the workload (default "
137*f40fafd4SAndroid Build Coastguard Worker          << !command.drop_state << ").\n";
138*f40fafd4SAndroid Build Coastguard Worker     ostr << "NOTE: -w WORKLOAD_T defines a new command and must come after its workload_options."
139*f40fafd4SAndroid Build Coastguard Worker          << std::endl;
140*f40fafd4SAndroid Build Coastguard Worker }
141*f40fafd4SAndroid Build Coastguard Worker 
drop_state()142*f40fafd4SAndroid Build Coastguard Worker void drop_state() {
143*f40fafd4SAndroid Build Coastguard Worker     // Drop inode/dentry/page caches.
144*f40fafd4SAndroid Build Coastguard Worker     std::system("sync; echo 3 > /proc/sys/vm/drop_caches");
145*f40fafd4SAndroid Build Coastguard Worker }
146*f40fafd4SAndroid Build Coastguard Worker 
147*f40fafd4SAndroid Build Coastguard Worker static constexpr int OPEN_DIR_FLAGS = O_RDONLY | O_DIRECTORY | O_PATH | O_CLOEXEC;
148*f40fafd4SAndroid Build Coastguard Worker 
delete_files(const std::string & dir,int n_file,const std::string & basename)149*f40fafd4SAndroid Build Coastguard Worker bool delete_files(const std::string& dir, int n_file, const std::string& basename) {
150*f40fafd4SAndroid Build Coastguard Worker     int dir_fd = open(dir.c_str(), OPEN_DIR_FLAGS);
151*f40fafd4SAndroid Build Coastguard Worker     if (dir_fd == -1) {
152*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
153*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open work directory '" << dir << "', error '" << strerror(error)
154*f40fafd4SAndroid Build Coastguard Worker                   << "'." << std::endl;
155*f40fafd4SAndroid Build Coastguard Worker         return false;
156*f40fafd4SAndroid Build Coastguard Worker     }
157*f40fafd4SAndroid Build Coastguard Worker 
158*f40fafd4SAndroid Build Coastguard Worker     bool ret = true;
159*f40fafd4SAndroid Build Coastguard Worker     for (int i = 0; i < n_file; i++) {
160*f40fafd4SAndroid Build Coastguard Worker         std::string filename = basename + std::to_string(i);
161*f40fafd4SAndroid Build Coastguard Worker         ret = ret && (unlinkat(dir_fd, filename.c_str(), 0) == 0);
162*f40fafd4SAndroid Build Coastguard Worker     }
163*f40fafd4SAndroid Build Coastguard Worker 
164*f40fafd4SAndroid Build Coastguard Worker     if (!ret) std::cerr << "Failed to delete at least one of the files" << std::endl;
165*f40fafd4SAndroid Build Coastguard Worker     close(dir_fd);
166*f40fafd4SAndroid Build Coastguard Worker     return ret;
167*f40fafd4SAndroid Build Coastguard Worker }
168*f40fafd4SAndroid Build Coastguard Worker 
create_files(const std::string & dir,int n_file,const std::string & basename)169*f40fafd4SAndroid Build Coastguard Worker bool create_files(const std::string& dir, int n_file, const std::string& basename) {
170*f40fafd4SAndroid Build Coastguard Worker     int dir_fd = open(dir.c_str(), OPEN_DIR_FLAGS);
171*f40fafd4SAndroid Build Coastguard Worker     if (dir_fd == -1) {
172*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
173*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open work directory '" << dir << "', error '" << strerror(error)
174*f40fafd4SAndroid Build Coastguard Worker                   << "'." << std::endl;
175*f40fafd4SAndroid Build Coastguard Worker         return false;
176*f40fafd4SAndroid Build Coastguard Worker     }
177*f40fafd4SAndroid Build Coastguard Worker 
178*f40fafd4SAndroid Build Coastguard Worker     bool ret = true;
179*f40fafd4SAndroid Build Coastguard Worker     for (int i = 0; i < n_file; i++) {
180*f40fafd4SAndroid Build Coastguard Worker         std::string filename = basename + std::to_string(i);
181*f40fafd4SAndroid Build Coastguard Worker         int fd = openat(dir_fd, filename.c_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0777);
182*f40fafd4SAndroid Build Coastguard Worker         ret = ret && fd != -1;
183*f40fafd4SAndroid Build Coastguard Worker         close(fd);
184*f40fafd4SAndroid Build Coastguard Worker     }
185*f40fafd4SAndroid Build Coastguard Worker 
186*f40fafd4SAndroid Build Coastguard Worker     close(dir_fd);
187*f40fafd4SAndroid Build Coastguard Worker     if (!ret) {
188*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open at least one of the files" << std::endl;
189*f40fafd4SAndroid Build Coastguard Worker         delete_files(dir, n_file, basename);
190*f40fafd4SAndroid Build Coastguard Worker     }
191*f40fafd4SAndroid Build Coastguard Worker     return ret;
192*f40fafd4SAndroid Build Coastguard Worker }
193*f40fafd4SAndroid Build Coastguard Worker 
move_files(const std::string & from_dir,const std::string & to_dir,int n_file,const std::string & from_basename,const std::string & to_basename)194*f40fafd4SAndroid Build Coastguard Worker bool move_files(const std::string& from_dir, const std::string& to_dir, int n_file,
195*f40fafd4SAndroid Build Coastguard Worker                 const std::string& from_basename, const std::string& to_basename) {
196*f40fafd4SAndroid Build Coastguard Worker     int from_dir_fd = open(from_dir.c_str(), OPEN_DIR_FLAGS);
197*f40fafd4SAndroid Build Coastguard Worker     if (from_dir_fd == -1) {
198*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
199*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open source directory '" << from_dir << "', error '"
200*f40fafd4SAndroid Build Coastguard Worker                   << strerror(error) << "'." << std::endl;
201*f40fafd4SAndroid Build Coastguard Worker         return false;
202*f40fafd4SAndroid Build Coastguard Worker     }
203*f40fafd4SAndroid Build Coastguard Worker     int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
204*f40fafd4SAndroid Build Coastguard Worker     if (to_dir_fd == -1) {
205*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
206*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
207*f40fafd4SAndroid Build Coastguard Worker                   << strerror(error) << "'." << std::endl;
208*f40fafd4SAndroid Build Coastguard Worker         close(from_dir_fd);
209*f40fafd4SAndroid Build Coastguard Worker         return false;
210*f40fafd4SAndroid Build Coastguard Worker     }
211*f40fafd4SAndroid Build Coastguard Worker 
212*f40fafd4SAndroid Build Coastguard Worker     bool ret = true;
213*f40fafd4SAndroid Build Coastguard Worker     for (int i = 0; i < n_file; i++) {
214*f40fafd4SAndroid Build Coastguard Worker         std::string from_filename = from_basename + std::to_string(i);
215*f40fafd4SAndroid Build Coastguard Worker         std::string to_filename = to_basename + std::to_string(i);
216*f40fafd4SAndroid Build Coastguard Worker         ret = ret &&
217*f40fafd4SAndroid Build Coastguard Worker               (renameat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str()) == 0);
218*f40fafd4SAndroid Build Coastguard Worker     }
219*f40fafd4SAndroid Build Coastguard Worker 
220*f40fafd4SAndroid Build Coastguard Worker     if (!ret) std::cerr << "Failed to move at least one of the files" << std::endl;
221*f40fafd4SAndroid Build Coastguard Worker     close(from_dir_fd);
222*f40fafd4SAndroid Build Coastguard Worker     close(from_dir_fd);
223*f40fafd4SAndroid Build Coastguard Worker     return ret;
224*f40fafd4SAndroid Build Coastguard Worker }
225*f40fafd4SAndroid Build Coastguard Worker 
hardlink_files(const std::string & from_dir,const std::string & to_dir,int n_file,const std::string & from_basename,const std::string & to_basename)226*f40fafd4SAndroid Build Coastguard Worker bool hardlink_files(const std::string& from_dir, const std::string& to_dir, int n_file,
227*f40fafd4SAndroid Build Coastguard Worker                     const std::string& from_basename, const std::string& to_basename) {
228*f40fafd4SAndroid Build Coastguard Worker     int from_dir_fd = open(from_dir.c_str(), OPEN_DIR_FLAGS);
229*f40fafd4SAndroid Build Coastguard Worker     if (from_dir_fd == -1) {
230*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
231*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open source directory '" << from_dir << "', error '"
232*f40fafd4SAndroid Build Coastguard Worker                   << strerror(error) << "'." << std::endl;
233*f40fafd4SAndroid Build Coastguard Worker         return false;
234*f40fafd4SAndroid Build Coastguard Worker     }
235*f40fafd4SAndroid Build Coastguard Worker     int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
236*f40fafd4SAndroid Build Coastguard Worker     if (to_dir_fd == -1) {
237*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
238*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
239*f40fafd4SAndroid Build Coastguard Worker                   << strerror(error) << "'." << std::endl;
240*f40fafd4SAndroid Build Coastguard Worker         close(from_dir_fd);
241*f40fafd4SAndroid Build Coastguard Worker         return false;
242*f40fafd4SAndroid Build Coastguard Worker     }
243*f40fafd4SAndroid Build Coastguard Worker 
244*f40fafd4SAndroid Build Coastguard Worker     bool ret = true;
245*f40fafd4SAndroid Build Coastguard Worker     for (int i = 0; i < n_file; i++) {
246*f40fafd4SAndroid Build Coastguard Worker         std::string from_filename = from_basename + std::to_string(i);
247*f40fafd4SAndroid Build Coastguard Worker         std::string to_filename = to_basename + std::to_string(i);
248*f40fafd4SAndroid Build Coastguard Worker         ret = ret &&
249*f40fafd4SAndroid Build Coastguard Worker               (linkat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str(), 0) == 0);
250*f40fafd4SAndroid Build Coastguard Worker     }
251*f40fafd4SAndroid Build Coastguard Worker 
252*f40fafd4SAndroid Build Coastguard Worker     if (!ret) std::cerr << "Failed to hardlink at least one of the files" << std::endl;
253*f40fafd4SAndroid Build Coastguard Worker     close(from_dir_fd);
254*f40fafd4SAndroid Build Coastguard Worker     close(to_dir_fd);
255*f40fafd4SAndroid Build Coastguard Worker     return ret;
256*f40fafd4SAndroid Build Coastguard Worker }
257*f40fafd4SAndroid Build Coastguard Worker 
symlink_files(std::string from_dir,const std::string & to_dir,int n_file,const std::string & from_basename,const std::string & to_basename)258*f40fafd4SAndroid Build Coastguard Worker bool symlink_files(std::string from_dir, const std::string& to_dir, int n_file,
259*f40fafd4SAndroid Build Coastguard Worker                    const std::string& from_basename, const std::string& to_basename) {
260*f40fafd4SAndroid Build Coastguard Worker     if (from_dir.back() != '/') from_dir.push_back('/');
261*f40fafd4SAndroid Build Coastguard Worker     int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
262*f40fafd4SAndroid Build Coastguard Worker     if (to_dir_fd == -1) {
263*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
264*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
265*f40fafd4SAndroid Build Coastguard Worker                   << strerror(error) << "'." << std::endl;
266*f40fafd4SAndroid Build Coastguard Worker         return false;
267*f40fafd4SAndroid Build Coastguard Worker     }
268*f40fafd4SAndroid Build Coastguard Worker 
269*f40fafd4SAndroid Build Coastguard Worker     bool ret = true;
270*f40fafd4SAndroid Build Coastguard Worker     for (int i = 0; i < n_file; i++) {
271*f40fafd4SAndroid Build Coastguard Worker         std::string from_filepath = from_dir + from_basename + std::to_string(i);
272*f40fafd4SAndroid Build Coastguard Worker         std::string to_filename = to_basename + std::to_string(i);
273*f40fafd4SAndroid Build Coastguard Worker         ret = ret && (symlinkat(from_filepath.c_str(), to_dir_fd, to_filename.c_str()) == 0);
274*f40fafd4SAndroid Build Coastguard Worker     }
275*f40fafd4SAndroid Build Coastguard Worker 
276*f40fafd4SAndroid Build Coastguard Worker     if (!ret) std::cerr << "Failed to symlink at least one of the files" << std::endl;
277*f40fafd4SAndroid Build Coastguard Worker     close(to_dir_fd);
278*f40fafd4SAndroid Build Coastguard Worker     return ret;
279*f40fafd4SAndroid Build Coastguard Worker }
280*f40fafd4SAndroid Build Coastguard Worker 
exhaustive_readdir(const std::string & from_dir)281*f40fafd4SAndroid Build Coastguard Worker bool exhaustive_readdir(const std::string& from_dir) {
282*f40fafd4SAndroid Build Coastguard Worker     DIR* dir = opendir(from_dir.c_str());
283*f40fafd4SAndroid Build Coastguard Worker     if (dir == nullptr) {
284*f40fafd4SAndroid Build Coastguard Worker         int error = errno;
285*f40fafd4SAndroid Build Coastguard Worker         std::cerr << "Failed to open working directory '" << from_dir << "', error '"
286*f40fafd4SAndroid Build Coastguard Worker                   << strerror(error) << "'." << std::endl;
287*f40fafd4SAndroid Build Coastguard Worker         return false;
288*f40fafd4SAndroid Build Coastguard Worker     }
289*f40fafd4SAndroid Build Coastguard Worker 
290*f40fafd4SAndroid Build Coastguard Worker     errno = 0;
291*f40fafd4SAndroid Build Coastguard Worker     while (readdir(dir) != nullptr)
292*f40fafd4SAndroid Build Coastguard Worker         ;
293*f40fafd4SAndroid Build Coastguard Worker     // In case of failure readdir returns nullptr and sets errno accordingly (to
294*f40fafd4SAndroid Build Coastguard Worker     // something != 0).
295*f40fafd4SAndroid Build Coastguard Worker     // In case of success readdir != nullptr and errno is not changed.
296*f40fafd4SAndroid Build Coastguard Worker     // Source: man 3 readdir.
297*f40fafd4SAndroid Build Coastguard Worker     bool ret = errno == 0;
298*f40fafd4SAndroid Build Coastguard Worker     closedir(dir);
299*f40fafd4SAndroid Build Coastguard Worker     return ret;
300*f40fafd4SAndroid Build Coastguard Worker }
301*f40fafd4SAndroid Build Coastguard Worker 
create_workload(Collector * collector,const Command & command)302*f40fafd4SAndroid Build Coastguard Worker void create_workload(Collector* collector, const Command& command) {
303*f40fafd4SAndroid Build Coastguard Worker     if (command.drop_state) drop_state();
304*f40fafd4SAndroid Build Coastguard Worker     collector->reset();
305*f40fafd4SAndroid Build Coastguard Worker     if (create_files(command.from_dir, command.n_file, command.from_basename))
306*f40fafd4SAndroid Build Coastguard Worker         collector->collect_metric(command.workload);
307*f40fafd4SAndroid Build Coastguard Worker 
308*f40fafd4SAndroid Build Coastguard Worker     delete_files(command.from_dir, command.n_file, command.from_basename);
309*f40fafd4SAndroid Build Coastguard Worker }
310*f40fafd4SAndroid Build Coastguard Worker 
delete_workload(Collector * collector,const Command & command)311*f40fafd4SAndroid Build Coastguard Worker void delete_workload(Collector* collector, const Command& command) {
312*f40fafd4SAndroid Build Coastguard Worker     if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
313*f40fafd4SAndroid Build Coastguard Worker 
314*f40fafd4SAndroid Build Coastguard Worker     if (command.drop_state) drop_state();
315*f40fafd4SAndroid Build Coastguard Worker     collector->reset();
316*f40fafd4SAndroid Build Coastguard Worker     if (delete_files(command.from_dir, command.n_file, command.from_basename))
317*f40fafd4SAndroid Build Coastguard Worker         collector->collect_metric(command.workload);
318*f40fafd4SAndroid Build Coastguard Worker }
319*f40fafd4SAndroid Build Coastguard Worker 
move_workload(Collector * collector,const Command & command)320*f40fafd4SAndroid Build Coastguard Worker void move_workload(Collector* collector, const Command& command) {
321*f40fafd4SAndroid Build Coastguard Worker     if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
322*f40fafd4SAndroid Build Coastguard Worker 
323*f40fafd4SAndroid Build Coastguard Worker     if (command.drop_state) drop_state();
324*f40fafd4SAndroid Build Coastguard Worker     collector->reset();
325*f40fafd4SAndroid Build Coastguard Worker     if (move_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
326*f40fafd4SAndroid Build Coastguard Worker                    command.to_basename))
327*f40fafd4SAndroid Build Coastguard Worker         collector->collect_metric(command.workload);
328*f40fafd4SAndroid Build Coastguard Worker 
329*f40fafd4SAndroid Build Coastguard Worker     delete_files(command.to_dir, command.n_file, command.to_basename);
330*f40fafd4SAndroid Build Coastguard Worker }
331*f40fafd4SAndroid Build Coastguard Worker 
hardlink_workload(Collector * collector,const Command & command)332*f40fafd4SAndroid Build Coastguard Worker void hardlink_workload(Collector* collector, const Command& command) {
333*f40fafd4SAndroid Build Coastguard Worker     if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
334*f40fafd4SAndroid Build Coastguard Worker 
335*f40fafd4SAndroid Build Coastguard Worker     if (command.drop_state) drop_state();
336*f40fafd4SAndroid Build Coastguard Worker     collector->reset();
337*f40fafd4SAndroid Build Coastguard Worker     if (hardlink_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
338*f40fafd4SAndroid Build Coastguard Worker                        command.to_basename))
339*f40fafd4SAndroid Build Coastguard Worker         collector->collect_metric(command.workload);
340*f40fafd4SAndroid Build Coastguard Worker 
341*f40fafd4SAndroid Build Coastguard Worker     delete_files(command.from_dir, command.n_file, command.from_basename);
342*f40fafd4SAndroid Build Coastguard Worker     delete_files(command.to_dir, command.n_file, command.to_basename);
343*f40fafd4SAndroid Build Coastguard Worker }
344*f40fafd4SAndroid Build Coastguard Worker 
symlink_workload(Collector * collector,const Command & command)345*f40fafd4SAndroid Build Coastguard Worker void symlink_workload(Collector* collector, const Command& command) {
346*f40fafd4SAndroid Build Coastguard Worker     if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
347*f40fafd4SAndroid Build Coastguard Worker 
348*f40fafd4SAndroid Build Coastguard Worker     if (command.drop_state) drop_state();
349*f40fafd4SAndroid Build Coastguard Worker     collector->reset();
350*f40fafd4SAndroid Build Coastguard Worker     if (symlink_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
351*f40fafd4SAndroid Build Coastguard Worker                       command.to_basename))
352*f40fafd4SAndroid Build Coastguard Worker         collector->collect_metric(command.workload);
353*f40fafd4SAndroid Build Coastguard Worker 
354*f40fafd4SAndroid Build Coastguard Worker     delete_files(command.to_dir, command.n_file, command.to_basename);
355*f40fafd4SAndroid Build Coastguard Worker     delete_files(command.from_dir, command.n_file, command.from_basename);
356*f40fafd4SAndroid Build Coastguard Worker }
357*f40fafd4SAndroid Build Coastguard Worker 
readdir_workload(Collector * collector,const Command & command)358*f40fafd4SAndroid Build Coastguard Worker void readdir_workload(Collector* collector, const Command& command) {
359*f40fafd4SAndroid Build Coastguard Worker     if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
360*f40fafd4SAndroid Build Coastguard Worker 
361*f40fafd4SAndroid Build Coastguard Worker     if (command.drop_state) drop_state();
362*f40fafd4SAndroid Build Coastguard Worker     collector->reset();
363*f40fafd4SAndroid Build Coastguard Worker     if (exhaustive_readdir(command.from_dir)) collector->collect_metric(command.workload);
364*f40fafd4SAndroid Build Coastguard Worker 
365*f40fafd4SAndroid Build Coastguard Worker     delete_files(command.from_dir, command.n_file, command.from_basename);
366*f40fafd4SAndroid Build Coastguard Worker }
367*f40fafd4SAndroid Build Coastguard Worker 
368*f40fafd4SAndroid Build Coastguard Worker using workload_executor_t = std::function<void(Collector*, const Command&)>;
369*f40fafd4SAndroid Build Coastguard Worker 
370*f40fafd4SAndroid Build Coastguard Worker std::unordered_map<std::string, workload_executor_t> executors = {
371*f40fafd4SAndroid Build Coastguard Worker         {Command::CREATE, create_workload},   {Command::DELETE, delete_workload},
372*f40fafd4SAndroid Build Coastguard Worker         {Command::MOVE, move_workload},       {Command::HARDLINK, hardlink_workload},
373*f40fafd4SAndroid Build Coastguard Worker         {Command::SYMLINK, symlink_workload}, {Command::READDIR, readdir_workload}};
374*f40fafd4SAndroid Build Coastguard Worker 
main(int argc,char ** argv)375*f40fafd4SAndroid Build Coastguard Worker int main(int argc, char** argv) {
376*f40fafd4SAndroid Build Coastguard Worker     std::vector<Command> commands;
377*f40fafd4SAndroid Build Coastguard Worker     Command command;
378*f40fafd4SAndroid Build Coastguard Worker     int opt;
379*f40fafd4SAndroid Build Coastguard Worker 
380*f40fafd4SAndroid Build Coastguard Worker     while ((opt = getopt(argc, argv, "hvpsw:d:f:t:n:")) != -1) {
381*f40fafd4SAndroid Build Coastguard Worker         switch (opt) {
382*f40fafd4SAndroid Build Coastguard Worker             case 'h':
383*f40fafd4SAndroid Build Coastguard Worker                 usage(std::cout, argv[0]);
384*f40fafd4SAndroid Build Coastguard Worker                 return EXIT_SUCCESS;
385*f40fafd4SAndroid Build Coastguard Worker             case 'v':
386*f40fafd4SAndroid Build Coastguard Worker                 print_version();
387*f40fafd4SAndroid Build Coastguard Worker                 return EXIT_SUCCESS;
388*f40fafd4SAndroid Build Coastguard Worker             case 'p':
389*f40fafd4SAndroid Build Coastguard Worker                 print_commands(commands);
390*f40fafd4SAndroid Build Coastguard Worker                 return EXIT_SUCCESS;
391*f40fafd4SAndroid Build Coastguard Worker             case 's':
392*f40fafd4SAndroid Build Coastguard Worker                 command.drop_state = false;
393*f40fafd4SAndroid Build Coastguard Worker                 break;
394*f40fafd4SAndroid Build Coastguard Worker             case 'w':
395*f40fafd4SAndroid Build Coastguard Worker                 command.workload = optarg;
396*f40fafd4SAndroid Build Coastguard Worker                 commands.push_back(command);
397*f40fafd4SAndroid Build Coastguard Worker                 command.reset();
398*f40fafd4SAndroid Build Coastguard Worker                 break;
399*f40fafd4SAndroid Build Coastguard Worker             case 'd':
400*f40fafd4SAndroid Build Coastguard Worker             case 'f':
401*f40fafd4SAndroid Build Coastguard Worker                 command.from_dir = optarg;
402*f40fafd4SAndroid Build Coastguard Worker                 break;
403*f40fafd4SAndroid Build Coastguard Worker             case 't':
404*f40fafd4SAndroid Build Coastguard Worker                 command.to_dir = optarg;
405*f40fafd4SAndroid Build Coastguard Worker                 break;
406*f40fafd4SAndroid Build Coastguard Worker             case 'n':
407*f40fafd4SAndroid Build Coastguard Worker                 command.n_file = std::stoi(optarg);
408*f40fafd4SAndroid Build Coastguard Worker                 break;
409*f40fafd4SAndroid Build Coastguard Worker             default:
410*f40fafd4SAndroid Build Coastguard Worker                 usage(std::cerr, argv[0]);
411*f40fafd4SAndroid Build Coastguard Worker                 return EXIT_FAILURE;
412*f40fafd4SAndroid Build Coastguard Worker         }
413*f40fafd4SAndroid Build Coastguard Worker     }
414*f40fafd4SAndroid Build Coastguard Worker 
415*f40fafd4SAndroid Build Coastguard Worker     Collector collector;
416*f40fafd4SAndroid Build Coastguard Worker     for (const Command& command : commands) {
417*f40fafd4SAndroid Build Coastguard Worker         auto executor = executors.find(command.workload);
418*f40fafd4SAndroid Build Coastguard Worker         if (executor == executors.end()) continue;
419*f40fafd4SAndroid Build Coastguard Worker         executor->second(&collector, command);
420*f40fafd4SAndroid Build Coastguard Worker     }
421*f40fafd4SAndroid Build Coastguard Worker     collector.report_metrics();
422*f40fafd4SAndroid Build Coastguard Worker 
423*f40fafd4SAndroid Build Coastguard Worker     return EXIT_SUCCESS;
424*f40fafd4SAndroid Build Coastguard Worker }
425