blob: dae8667d1f7681ec809631f335aeb9b0df83531f [file] [log] [blame]
Adam Snaidere104ebf2023-06-05 17:42:06 -07001#include <chrono>
2
Austin Schuh99f7c6a2024-06-25 22:07:44 -07003#include "absl/flags/flag.h"
4#include "absl/flags/usage.h"
5#include "absl/log/check.h"
6#include "absl/log/log.h"
Adam Snaidere104ebf2023-06-05 17:42:06 -07007
8#include "aos/events/shm_event_loop.h"
9#include "aos/init.h"
10#include "aos/network/sctp_client.h"
Adam Snaider96a0f4b2023-05-18 20:41:19 -070011#include "aos/network/sctp_lib.h"
Adam Snaidere104ebf2023-06-05 17:42:06 -070012#include "aos/network/sctp_server.h"
13
Adam Snaider13d48d92023-08-03 12:20:15 -070014// The casts required to read datastructures from sockets trip - Wcast - align.
15#ifdef __clang
16#pragma clang diagnostic ignored "-Wcast-align"
17#endif
18
Austin Schuh99f7c6a2024-06-25 22:07:44 -070019ABSL_FLAG(std::string, config, "aos_config.json", "Path to the config.");
20ABSL_FLAG(uint32_t, port, 1323, "Port to run the sctp test on");
21ABSL_FLAG(uint32_t, payload_size, 1000, "Size of data to send in bytes");
22ABSL_FLAG(uint32_t, ttl, 0, "TTL in milliseconds");
23ABSL_FLAG(uint32_t, rx_size, 1000000,
24 "RX buffer size to set the max size to be in bytes.");
25ABSL_FLAG(std::string, host, "", "Server host (acts as server if unspecified)");
Adam Snaidere104ebf2023-06-05 17:42:06 -070026
Austin Schuh99f7c6a2024-06-25 22:07:44 -070027ABSL_FLAG(bool, client, false,
28 "If true, then act as a client, otherwise act as a server");
29ABSL_FLAG(uint32_t, skip_first_n, 10,
30 "Skip the first 'n' messages when computing statistics.");
Adam Snaidere104ebf2023-06-05 17:42:06 -070031
Austin Schuh99f7c6a2024-06-25 22:07:44 -070032ABSL_FLAG(std::string, sctp_auth_key_file, "",
33 "When set, use the provided key for SCTP authentication as "
34 "defined in RFC 4895");
Adam Snaider96a0f4b2023-05-18 20:41:19 -070035
Austin Schuh99f7c6a2024-06-25 22:07:44 -070036ABSL_DECLARE_FLAG(bool, die_on_malloc);
Adam Snaidere104ebf2023-06-05 17:42:06 -070037
38namespace aos::message_bridge::perf {
39
Adam Snaider96a0f4b2023-05-18 20:41:19 -070040namespace {
41
42using util::ReadFileToVecOrDie;
43
Adam Snaider9bb33442023-06-26 16:31:37 -070044SctpAuthMethod SctpAuthMethod() {
Austin Schuh99f7c6a2024-06-25 22:07:44 -070045 return absl::GetFlag(FLAGS_sctp_auth_key_file).empty()
46 ? SctpAuthMethod::kNoAuth
47 : SctpAuthMethod::kAuth;
Adam Snaider9bb33442023-06-26 16:31:37 -070048}
49
Adam Snaider96a0f4b2023-05-18 20:41:19 -070050std::vector<uint8_t> GetSctpAuthKey() {
Adam Snaider9bb33442023-06-26 16:31:37 -070051 if (SctpAuthMethod() == SctpAuthMethod::kNoAuth) {
52 return {};
Adam Snaider96a0f4b2023-05-18 20:41:19 -070053 }
Austin Schuh99f7c6a2024-06-25 22:07:44 -070054 return ReadFileToVecOrDie(absl::GetFlag(FLAGS_sctp_auth_key_file));
Adam Snaider96a0f4b2023-05-18 20:41:19 -070055}
56
57} // namespace
58
Adam Snaidere104ebf2023-06-05 17:42:06 -070059namespace chrono = std::chrono;
60
61class Server {
62 public:
63 Server(aos::ShmEventLoop *event_loop)
Adam Snaider96a0f4b2023-05-18 20:41:19 -070064 : event_loop_(event_loop),
Austin Schuh99f7c6a2024-06-25 22:07:44 -070065 server_(2, "0.0.0.0", absl::GetFlag(FLAGS_port), SctpAuthMethod()) {
Adam Snaider9bb33442023-06-26 16:31:37 -070066 server_.SetAuthKey(GetSctpAuthKey());
Adam Snaidere104ebf2023-06-05 17:42:06 -070067 event_loop_->epoll()->OnReadable(server_.fd(),
68 [this]() { MessageReceived(); });
Austin Schuh99f7c6a2024-06-25 22:07:44 -070069 server_.SetMaxReadSize(absl::GetFlag(FLAGS_rx_size) + 100);
70 server_.SetMaxWriteSize(absl::GetFlag(FLAGS_rx_size) + 100);
Adam Snaidere104ebf2023-06-05 17:42:06 -070071
72 event_loop_->SetRuntimeRealtimePriority(5);
73 }
74
75 ~Server() { event_loop_->epoll()->DeleteFd(server_.fd()); }
76
77 void SendMessage(std::string_view message) {
78 if (sac_assoc_id_ == 0) {
79 LOG(INFO) << "Lost connection to client. Not sending";
80 return;
81 }
Austin Schuh99f7c6a2024-06-25 22:07:44 -070082 if (server_.Send(message, sac_assoc_id_, 0, absl::GetFlag(FLAGS_ttl))) {
Adam Snaidere104ebf2023-06-05 17:42:06 -070083 LOG(INFO) << "Server reply with " << message.size() << "B";
84 } else {
85 PLOG(FATAL) << "Failed to send";
86 }
87 }
88
89 void MessageReceived() {
90 LOG(INFO) << "Received message";
91 aos::unique_c_ptr<Message> message = server_.Read();
92 if (!message) {
93 return;
94 }
95
96 if (message->message_type == Message::kNotification) {
97 const union sctp_notification *snp =
98 (const union sctp_notification *)message->data();
99
100 if (VLOG_IS_ON(2)) {
101 PrintNotification(message.get());
102 }
103
104 switch (snp->sn_header.sn_type) {
105 case SCTP_ASSOC_CHANGE: {
106 const struct sctp_assoc_change *sac = &snp->sn_assoc_change;
107 switch (sac->sac_state) {
108 case SCTP_COMM_UP:
109 NodeConnected(sac->sac_assoc_id);
110 VLOG(1) << "Peer connected";
111 break;
112 case SCTP_COMM_LOST:
113 case SCTP_SHUTDOWN_COMP:
114 case SCTP_CANT_STR_ASSOC:
115 NodeDisconnected(sac->sac_assoc_id);
116 VLOG(1) << "Disconnect";
117 break;
118 case SCTP_RESTART:
119 LOG(FATAL) << "Never seen this before.";
120 break;
121 }
122 } break;
123 }
124 } else if (message->message_type == Message::kMessage) {
125 SendMessage(
126 std::string_view((const char *)message->data(), message->size));
127 }
128 }
129
130 void NodeConnected(sctp_assoc_t assoc_id) {
131 sac_assoc_id_ = assoc_id;
132 server_.SetPriorityScheduler(assoc_id);
133 }
134 void NodeDisconnected(sctp_assoc_t /*assoc_id*/) { sac_assoc_id_ = 0; }
135
136 private:
137 sctp_assoc_t sac_assoc_id_ = 0;
138 aos::ShmEventLoop *event_loop_;
139 SctpServer server_;
140};
141
142class Client {
143 public:
144 Client(aos::ShmEventLoop *event_loop)
Adam Snaider96a0f4b2023-05-18 20:41:19 -0700145 : event_loop_(event_loop),
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700146 client_(absl::GetFlag(FLAGS_host), absl::GetFlag(FLAGS_port), 2,
147 "0.0.0.0", absl::GetFlag(FLAGS_port), SctpAuthMethod()) {
Adam Snaider9bb33442023-06-26 16:31:37 -0700148 client_.SetAuthKey(GetSctpAuthKey());
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700149 client_.SetMaxReadSize(absl::GetFlag(FLAGS_rx_size) + 100);
150 client_.SetMaxWriteSize(absl::GetFlag(FLAGS_rx_size) + 100);
Adam Snaidere104ebf2023-06-05 17:42:06 -0700151
152 timer_ = event_loop_->AddTimer([this]() { Ping(); });
153
154 event_loop_->OnRun([this]() {
155 timer_->Schedule(event_loop_->monotonic_now(),
156 chrono::milliseconds(1000));
157 });
158
159 event_loop_->epoll()->OnReadable(client_.fd(),
160 [this]() { MessageReceived(); });
161 event_loop_->SetRuntimeRealtimePriority(5);
162 }
163
164 ~Client() { event_loop_->epoll()->DeleteFd(client_.fd()); }
165
166 void Ping() {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700167 std::string payload(absl::GetFlag(FLAGS_payload_size), 'a');
Adam Snaidere104ebf2023-06-05 17:42:06 -0700168 sent_time_ = aos::monotonic_clock::now();
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700169 if (client_.Send(0, payload, absl::GetFlag(FLAGS_ttl))) {
Adam Snaidere104ebf2023-06-05 17:42:06 -0700170 LOG(INFO) << "Sending " << payload.size() << "B";
171 } else {
172 PLOG(ERROR) << "Failed to send";
173 }
174 }
175
176 void MessageReceived() {
177 aos::unique_c_ptr<Message> message = client_.Read();
178 if (!message) {
179 return;
180 }
181
182 if (message->message_type == Message::kNotification) {
183 const union sctp_notification *snp =
184 (const union sctp_notification *)message->data();
185
186 if (VLOG_IS_ON(2)) {
187 PrintNotification(message.get());
188 }
189
190 switch (snp->sn_header.sn_type) {
191 case SCTP_ASSOC_CHANGE: {
192 const struct sctp_assoc_change *sac = &snp->sn_assoc_change;
193 switch (sac->sac_state) {
194 case SCTP_COMM_UP:
195 NodeConnected(sac->sac_assoc_id);
196 VLOG(1) << "Peer connected";
197 break;
198 case SCTP_COMM_LOST:
199 case SCTP_SHUTDOWN_COMP:
200 case SCTP_CANT_STR_ASSOC:
201 NodeDisconnected(sac->sac_assoc_id);
202 VLOG(1) << "Disconnect";
203 break;
204 case SCTP_RESTART:
205 LOG(FATAL) << "Never seen this before.";
206 break;
207 }
208 } break;
209 }
210 } else if (message->message_type == Message::kMessage) {
211 HandleData(message.get());
212 }
213 }
214
215 void NodeConnected(sctp_assoc_t assoc_id) {
216 client_.SetPriorityScheduler(assoc_id);
217 }
218 void NodeDisconnected(sctp_assoc_t /*assoc_id*/) {}
219
220 void HandleData(const Message *) {
221 count_++;
222 if (count_ <= 0) {
223 LOG(INFO) << "Got message: Skipping " << -count_;
224 return;
225 }
226 auto elapsed = aos::monotonic_clock::now() - sent_time_;
227 double elapsed_secs =
228 std::chrono::duration_cast<std::chrono::duration<double>>(elapsed)
229 .count();
230 avg_latency_ = (avg_latency_ * (count_ - 1) + elapsed_secs) / count_;
231 // average one-way throughput
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700232 double throughput = absl::GetFlag(FLAGS_payload_size) * 2.0 / elapsed_secs;
233 double avg_throughput =
234 absl::GetFlag(FLAGS_payload_size) * 2.0 / avg_latency_;
Adam Snaidere104ebf2023-06-05 17:42:06 -0700235 printf(
Adam Snaider96a0f4b2023-05-18 20:41:19 -0700236 "Round trip: %.2fms | %.2f KB/s | Avg RTL: %.2fms | %.2f KB/s | "
237 "Count: %d\n",
Adam Snaidere104ebf2023-06-05 17:42:06 -0700238 elapsed_secs * 1000, throughput / 1024, avg_latency_ * 1000,
239 avg_throughput / 1024, count_);
240 }
241
242 private:
243 aos::ShmEventLoop *event_loop_;
244 SctpClient client_;
245 aos::TimerHandler *timer_;
246 double avg_latency_ = 0.0;
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700247 int count_ = -absl::GetFlag(FLAGS_skip_first_n);
Adam Snaidere104ebf2023-06-05 17:42:06 -0700248
249 aos::monotonic_clock::time_point sent_time_;
250};
251
252int Main() {
253 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700254 aos::configuration::ReadConfig(absl::GetFlag(FLAGS_config));
Adam Snaidere104ebf2023-06-05 17:42:06 -0700255
256 aos::ShmEventLoop event_loop(&config.message());
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700257 if (absl::GetFlag(FLAGS_client)) {
258 CHECK(!absl::GetFlag(FLAGS_host).empty())
259 << "Client Usage: `sctp_perf --client --host "
260 "abc.com --payload_size [bytes] "
261 "[--port PORT] [--config PATH]`";
Adam Snaidere104ebf2023-06-05 17:42:06 -0700262
263 Client client(&event_loop);
264 event_loop.Run();
265 } else {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700266 CHECK(absl::GetFlag(FLAGS_host).empty())
267 << "Server Usage: `sctp_perf [--config PATH]`";
Adam Snaidere104ebf2023-06-05 17:42:06 -0700268 Server server(&event_loop);
269 event_loop.Run();
270 }
271
272 return EXIT_SUCCESS;
273}
274
275} // namespace aos::message_bridge::perf
276
277int main(int argc, char **argv) {
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700278 absl::SetProgramUsageMessage(absl::StrCat(
Adam Snaidere104ebf2023-06-05 17:42:06 -0700279 "Measure SCTP performance\n", " Server Usage: `sctp_perf`\n",
280 " Client Usage: `sctp_perf --client --host abc.com`\n"));
281 aos::InitGoogle(&argc, &argv);
282
283 // Client and server need to malloc.
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700284 absl::SetFlag(&FLAGS_die_on_malloc, false);
Adam Snaidere104ebf2023-06-05 17:42:06 -0700285 return aos::message_bridge::perf::Main();
286}