blob: 030852ca4573094b2943ffd19be726acba031e03 [file] [log] [blame]
Austin Schuh5af45eb2019-09-16 20:54:18 -07001#include "gflags/gflags.h"
2
3#include <sys/stat.h>
4#include <sys/types.h>
5#include <chrono>
6#include <random>
7#include <thread>
8
9#include "aos/events/epoll.h"
10#include "aos/init.h"
11#include "aos/ipc_lib/latency_lib.h"
12#include "aos/logging/implementations.h"
13#include "aos/logging/logging.h"
14#include "aos/realtime.h"
15#include "aos/time/time.h"
16
17// This is a demo program which uses named pipes to communicate.
18// It measures both latency of a random timer thread, and latency of the
19// pipe.
20
21DEFINE_bool(sender, true, "If true, send signals to the other process.");
Austin Schuha0c41ba2020-09-10 22:59:14 -070022DEFINE_string(fifo, "/dev/shm/aos/named_pipe_latency",
23 "FIFO to use for the test.");
Austin Schuh5af45eb2019-09-16 20:54:18 -070024DEFINE_int32(seconds, 10, "Duration of the test to run");
25DEFINE_int32(
26 latency_threshold, 1000,
27 "Disable tracing when anything takes more than this many microseoncds");
28DEFINE_int32(core, 7, "Core to pin to");
29DEFINE_int32(sender_priority, 53, "RT priority to send at");
30DEFINE_int32(receiver_priority, 52, "RT priority to receive at");
31DEFINE_int32(timer_priority, 51, "RT priority to spin the timer at");
32
33DEFINE_bool(log_latency, false, "If true, log the latency");
34
35namespace chrono = ::std::chrono;
36
37namespace aos {
38
39void SenderThread() {
Austin Schuha0c41ba2020-09-10 22:59:14 -070040 int pipefd =
41 open(FLAGS_fifo.c_str(), FD_CLOEXEC | O_NONBLOCK | O_WRONLY | O_NOATIME);
Austin Schuh5af45eb2019-09-16 20:54:18 -070042 const monotonic_clock::time_point end_time =
43 monotonic_clock::now() + chrono::seconds(FLAGS_seconds);
44 // Standard mersenne_twister_engine seeded with 0
45 ::std::mt19937 generator(0);
46
47 // Sleep between 1 and 15 ms.
48 ::std::uniform_int_distribution<> distribution(1000, 15000);
49
Austin Schuh094d09b2020-11-20 23:26:52 -080050 {
51 cpu_set_t cpuset;
52 CPU_ZERO(&cpuset);
53 CPU_SET(FLAGS_core, &cpuset);
54
55 SetCurrentThreadAffinity(cpuset);
56 }
Austin Schuh5af45eb2019-09-16 20:54:18 -070057 SetCurrentThreadRealtimePriority(FLAGS_sender_priority);
58 while (true) {
59 const monotonic_clock::time_point wakeup_time =
60 monotonic_clock::now() + chrono::microseconds(distribution(generator));
61
62 ::std::this_thread::sleep_until(wakeup_time);
63 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
64 char sent_time_buffer[8];
65 memcpy(sent_time_buffer, &monotonic_now, sizeof(sent_time_buffer));
66 PCHECK(write(pipefd, static_cast<void *>(sent_time_buffer),
67 sizeof(sent_time_buffer)));
68
69 if (monotonic_now > end_time) {
70 break;
71 }
72 }
73
74 {
75 char sent_time_buffer[8];
76 memset(sent_time_buffer, 0, sizeof(sent_time_buffer));
77 PCHECK(write(pipefd, static_cast<void *>(sent_time_buffer),
78 sizeof(sent_time_buffer)));
79 }
80 UnsetCurrentThreadRealtimePriority();
81
82 PCHECK(close(pipefd));
83}
84
85void ReceiverThread() {
Austin Schuha0c41ba2020-09-10 22:59:14 -070086 int pipefd =
87 open(FLAGS_fifo.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY | O_NOATIME);
Austin Schuh5af45eb2019-09-16 20:54:18 -070088 Tracing t;
89 t.Start();
90
91 chrono::nanoseconds max_wakeup_latency = chrono::nanoseconds(0);
92
93 chrono::nanoseconds sum_latency = chrono::nanoseconds(0);
94 int latency_count = 0;
95
96 internal::EPoll epoll;
97
98 epoll.OnReadable(pipefd, [&t, &epoll, &max_wakeup_latency, &sum_latency,
99 &latency_count, pipefd]() {
100 char sent_time_buffer[8];
101 const int ret = read(pipefd, static_cast<void *>(sent_time_buffer),
102 sizeof(sent_time_buffer));
103 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
104 CHECK_EQ(ret, 8);
105
106 monotonic_clock::time_point sent_time;
107 memcpy(&sent_time, sent_time_buffer, sizeof(sent_time_buffer));
108
109 if (sent_time == monotonic_clock::epoch()) {
110 epoll.Quit();
111 return;
112 }
113
114 const chrono::nanoseconds wakeup_latency = monotonic_now - sent_time;
115
116 sum_latency += wakeup_latency;
117 ++latency_count;
118
119 max_wakeup_latency = ::std::max(wakeup_latency, max_wakeup_latency);
120
121 if (wakeup_latency > chrono::microseconds(FLAGS_latency_threshold)) {
122 t.Stop();
123 AOS_LOG(INFO, "Stopped tracing, latency %" PRId64 "\n",
124 static_cast<int64_t>(wakeup_latency.count()));
125 }
126
127 if (FLAGS_log_latency) {
128 AOS_LOG(INFO, "dt: %8d.%03d\n",
129 static_cast<int>(wakeup_latency.count() / 1000),
130 static_cast<int>(wakeup_latency.count() % 1000));
131 }
132 });
133
Austin Schuh094d09b2020-11-20 23:26:52 -0800134 {
135 cpu_set_t cpuset;
136 CPU_ZERO(&cpuset);
137 CPU_SET(FLAGS_core, &cpuset);
138
139 SetCurrentThreadAffinity(cpuset);
140 }
Austin Schuh5af45eb2019-09-16 20:54:18 -0700141 SetCurrentThreadRealtimePriority(FLAGS_receiver_priority);
142 epoll.Run();
143 UnsetCurrentThreadRealtimePriority();
144 epoll.DeleteFd(pipefd);
145
146 const chrono::nanoseconds average_latency = sum_latency / latency_count;
147
148 AOS_LOG(
149 INFO,
150 "Max named pip wakeup latency: %d.%03d microseconds, average: %d.%03d "
151 "microseconds\n",
152 static_cast<int>(max_wakeup_latency.count() / 1000),
153 static_cast<int>(max_wakeup_latency.count() % 1000),
154 static_cast<int>(average_latency.count() / 1000),
155 static_cast<int>(average_latency.count() % 1000));
156
157 PCHECK(close(pipefd));
158}
159
160int Main(int /*argc*/, char ** /*argv*/) {
161 mkfifo(FLAGS_fifo.c_str(), 0777);
162
163 AOS_LOG(INFO, "Main!\n");
164 ::std::thread t([]() {
165 TimerThread(monotonic_clock::now() + chrono::seconds(FLAGS_seconds),
166 FLAGS_timer_priority);
167 });
168
Austin Schuha0c41ba2020-09-10 22:59:14 -0700169 ::std::thread st([]() { SenderThread(); });
Austin Schuh5af45eb2019-09-16 20:54:18 -0700170
171 ReceiverThread();
172 st.join();
173
174 t.join();
175 return 0;
176}
177
178} // namespace aos
179
180int main(int argc, char **argv) {
181 ::gflags::ParseCommandLineFlags(&argc, &argv, true);
182
Austin Schuh5af45eb2019-09-16 20:54:18 -0700183 return ::aos::Main(argc, argv);
184}