Separate benchmark tool from rpc-port
[platform/core/appfw/rpc-port.git] / benchmark / tool / main.cc
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <glib.h>
18 #include <signal.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <unistd.h>
22
23 #include <chrono>
24 #include <ctime>
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28 #include <string>
29
30 #include "BenchmarkProxy.h"
31 #include "dbus-proxy.hh"
32 #include "grpc-proxy.hh"
33 #include "log-private.hh"
34 #include "options.hh"
35
36 namespace {
37
38 #define SERVER_PROC_NAME "d::org.tizen.appfw.rpc_port.benchmark"
39 #define SERVER_BIN_TIDL "/usr/bin/rpc-port-benchmark-server-tidl"
40 #define SERVER_BIN_DBUS "/usr/bin/rpc-port-benchmark-server-dbus"
41 #define SERVER_BIN_GRPC "/usr/bin/rpc-port-benchmark-server-grpc"
42
43 namespace bp = rpc_port::BenchmarkProxy::proxy;
44
45 class ConnectionListener : public bp::Benchmark::IEventListener {
46  public:
47   void OnConnected() override {
48   }
49
50   void OnDisconnected() override {
51   }
52
53   void OnRejected() override {
54   }
55 };
56
57 class Tester {
58  public:
59   Tester() {
60   }
61
62   ~Tester() {
63     if (server_pid_ > 0) {
64       if (kill(server_pid_, SIGTERM) != 0) {
65         std::cerr << "kill() is failed. errno: " << errno << std::endl;
66       } else {
67         int status;
68         waitpid(server_pid_, &status, 0);
69       }
70     }
71   }
72
73   void Run(int argc, char** argv) {
74     options_ = rpc_port::benchmark::Options::Parse(argc, argv);
75     if (!options_) {
76       std::cerr << "options is unllptr" << std::endl;
77       return;
78     }
79
80     if (options_->ShouldPrintTime()) PrintStartTime();
81
82     ExecuteServer();
83     if (options_->IsDbus()) {
84       dbus_proxy_.Connect();
85     } else if (options_->IsGrpc()) {
86       grpc_proxy_.Connect();
87     } else {
88       std::string proc_name(SERVER_PROC_NAME);
89       if (getuid() >= 5000) proc_name = "u" + proc_name;
90
91       proxy_.reset(new bp::Benchmark(&listener_, proc_name.c_str()));
92
93       try {
94         proxy_->Connect(true);
95       } catch (const bp::Exception& e) {
96         std::cerr << "Connect() failed" << std::endl;
97         return;
98       } catch (const std::exception& e) {
99         std::cerr << "Connect() failed. " << e.what() << std::endl;
100         return;
101       }
102     }
103
104     printf("%15s\t%15s\t%15s\t\t%15s\t\t%15s\n", "Iterations", "Data size",
105         "Time", "Throughput", "Latency");
106
107     try {
108       if (options_->IsAll()) {
109         DoTest(4000, 10);
110         DoTest(4000, 20);
111         DoTest(4000, 100);
112         DoTest(4000, 200);
113         DoTest(2000, 1000);
114         DoTest(2000, 2000);
115         DoTest(1000, 10000);
116         DoTest(1000, 20000);
117         DoTest(1000, 30000);
118         DoTest(1000, 40000);
119         DoTest(1000, 50000);
120         DoTest(1000, 60000);
121         DoTest(500, 100000);
122       } else {
123         DoTest(options_->GetIters(), options_->GetSize());
124       }
125     } catch (const bp::Exception& e) {
126       std::cerr << "test failed" << std::endl;
127     }
128   }
129
130  private:
131   void DoTest(int iters, int size) {
132     bool is_func = options_->IsFunction();
133     bool is_dbus = options_->IsDbus();
134     bool is_grpc = options_->IsGrpc();
135
136     if (is_dbus) dbus_proxy_.Start(getpid());
137
138     StartTime();
139     for (int i = 0; i < iters; i++) {
140       if (is_func) {
141         int ret = FakeFunction(std::string(size, 'a'));
142         if (ret != 0) {
143           std::cerr << "Invalid return" << std::endl;
144           break;
145         }
146
147         continue;
148       }
149
150       if (is_dbus) {
151         int ret = dbus_proxy_.Test(std::string(size, 'a'));
152         if (ret != 0) {
153           std::cerr << "Invalid return" << std::endl;
154           break;
155         }
156
157         continue;
158       }
159
160       if (is_grpc) {
161         int ret = grpc_proxy_.Test(std::string(size, 'a'));
162         if (ret != 0) {
163           std::cerr << "Invalid return" << std::endl;
164           break;
165         }
166
167         continue;
168       }
169
170       int ret = proxy_->Test(std::string(size, 'a'));
171       if (ret != 0) {
172         std::cerr << "Invalid return" << std::endl;
173         break;
174       }
175     }
176     EndTime(iters, size);
177
178     if (is_dbus) dbus_proxy_.Stop(getpid());
179   }
180
181   int FakeFunction(std::string str) {
182     return 0;
183   }
184
185   void StartTime() {
186     start_ = std::chrono::system_clock::now();
187   }
188
189   void EndTime(int iters, int size) {
190     std::chrono::duration<double> sec =
191         std::chrono::system_clock::now() - start_;
192     double t = size * iters * 8 / sec.count() / 1024 / 1024;
193     double l = sec.count() * 1000 / iters;
194
195     printf("%10d\t%10dByte\t%15.4fs\t%15.4fMb/s\t%15.4fms\n", iters, size,
196         sec.count(), t, l);
197   }
198
199   void ExecuteServer() {
200     bool is_dbus = options_->IsDbus();
201     bool is_grpc = options_->IsGrpc();
202     if (!is_grpc) return;
203
204     server_pid_ = fork();
205     if (server_pid_ == 0) {
206       setsid();
207       int ret;
208       if (is_dbus) {
209         char bin[] = { SERVER_BIN_DBUS };
210         char* argv[] = { bin, nullptr, nullptr };
211         ret = execv(argv[0], argv);
212       } else if (is_grpc) {
213         char bin[] = { SERVER_BIN_GRPC };
214         char* argv[] = { bin, nullptr, nullptr };
215         ret = execv(argv[0], argv);
216       } else {
217         char bin[] = { SERVER_BIN_TIDL };
218         char* argv[] = { bin, nullptr, nullptr };
219         ret = execv(argv[0], argv);
220       }
221       if (ret < 0) {
222         std::cerr << "execv() is failed. errno: " << errno << std::endl;
223         exit(-1);
224       }
225     } else if (server_pid_ == -1) {
226       std::cerr << "fork() is failed. errno: " << errno << std::endl;
227       exit(-1);
228     } else {
229       _W("benchmark server is running. pid: %d", server_pid_);
230       usleep(100 * 1000);
231     }
232   }
233
234   void PrintStartTime() {
235     std::ifstream stat_file("/proc/self/stat");
236     if (!stat_file) {
237       _E("Failed to open stat");
238       return;
239     }
240
241     std::string line;
242     getline(stat_file, line);
243     std::istringstream iss(line);
244
245     std::string value;
246     int pos = 21;
247     for (int i = 0; i < pos; ++i) iss >> value;
248
249     iss >> value;
250     stat_file.close();
251
252     long start_time = std::stol(value) / sysconf(_SC_CLK_TCK);
253     time_t start_time_seconds = start_time;
254     long start_time_microseconds = (start_time - start_time_seconds) * 1000000;
255
256     std::time_t start_time_utc = std::time(nullptr) - start_time_seconds;
257     std::tm* start_time_tm = std::localtime(&start_time_utc);
258
259     char buffer[26];
260     std::strftime(buffer, sizeof(buffer), "%Y-%m %H:%M:%S", start_time_tm);
261
262     std::string result = buffer;
263     result += ".";
264     result += std::to_string(start_time_microseconds);
265     result += " UTC";
266
267     std::cout << "Program start time: [" << value << "] " << result
268               << std::endl;
269   }
270
271  private:
272   std::unique_ptr<rpc_port::benchmark::Options> options_;
273   std::unique_ptr<bp::Benchmark> proxy_;
274   ConnectionListener listener_;
275   std::chrono::system_clock::time_point start_;
276   pid_t server_pid_ = -1;
277   rpc_port::benchmark::DbusProxy dbus_proxy_;
278   rpc_port::benchmark::GrpcProxy grpc_proxy_;
279 };
280
281 }  // namespace
282
283 int main(int argc, char** argv) {
284   try {
285     Tester tester;
286     tester.Run(argc, argv);
287   } catch (const rpc_port::BenchmarkProxy::proxy::InvalidIOException& ie) {
288     _E("InvalidIOException occurs");
289     return -1;
290   } catch (const std::bad_weak_ptr& be) {
291     _E("bad_weak_ptr occurs. error(%s)", be.what());
292     return -1;
293   }
294
295   return 0;
296 }