blob: c3f5c5ca2976844a434f5b04af3ef4651764c5b2 [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
50 PinCurrentThreadToCPU(FLAGS_core);
51 SetCurrentThreadRealtimePriority(FLAGS_sender_priority);
52 while (true) {
53 const monotonic_clock::time_point wakeup_time =
54 monotonic_clock::now() + chrono::microseconds(distribution(generator));
55
56 ::std::this_thread::sleep_until(wakeup_time);
57 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
58 char sent_time_buffer[8];
59 memcpy(sent_time_buffer, &monotonic_now, sizeof(sent_time_buffer));
60 PCHECK(write(pipefd, static_cast<void *>(sent_time_buffer),
61 sizeof(sent_time_buffer)));
62
63 if (monotonic_now > end_time) {
64 break;
65 }
66 }
67
68 {
69 char sent_time_buffer[8];
70 memset(sent_time_buffer, 0, sizeof(sent_time_buffer));
71 PCHECK(write(pipefd, static_cast<void *>(sent_time_buffer),
72 sizeof(sent_time_buffer)));
73 }
74 UnsetCurrentThreadRealtimePriority();
75
76 PCHECK(close(pipefd));
77}
78
79void ReceiverThread() {
Austin Schuha0c41ba2020-09-10 22:59:14 -070080 int pipefd =
81 open(FLAGS_fifo.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY | O_NOATIME);
Austin Schuh5af45eb2019-09-16 20:54:18 -070082 Tracing t;
83 t.Start();
84
85 chrono::nanoseconds max_wakeup_latency = chrono::nanoseconds(0);
86
87 chrono::nanoseconds sum_latency = chrono::nanoseconds(0);
88 int latency_count = 0;
89
90 internal::EPoll epoll;
91
92 epoll.OnReadable(pipefd, [&t, &epoll, &max_wakeup_latency, &sum_latency,
93 &latency_count, pipefd]() {
94 char sent_time_buffer[8];
95 const int ret = read(pipefd, static_cast<void *>(sent_time_buffer),
96 sizeof(sent_time_buffer));
97 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
98 CHECK_EQ(ret, 8);
99
100 monotonic_clock::time_point sent_time;
101 memcpy(&sent_time, sent_time_buffer, sizeof(sent_time_buffer));
102
103 if (sent_time == monotonic_clock::epoch()) {
104 epoll.Quit();
105 return;
106 }
107
108 const chrono::nanoseconds wakeup_latency = monotonic_now - sent_time;
109
110 sum_latency += wakeup_latency;
111 ++latency_count;
112
113 max_wakeup_latency = ::std::max(wakeup_latency, max_wakeup_latency);
114
115 if (wakeup_latency > chrono::microseconds(FLAGS_latency_threshold)) {
116 t.Stop();
117 AOS_LOG(INFO, "Stopped tracing, latency %" PRId64 "\n",
118 static_cast<int64_t>(wakeup_latency.count()));
119 }
120
121 if (FLAGS_log_latency) {
122 AOS_LOG(INFO, "dt: %8d.%03d\n",
123 static_cast<int>(wakeup_latency.count() / 1000),
124 static_cast<int>(wakeup_latency.count() % 1000));
125 }
126 });
127
128 PinCurrentThreadToCPU(FLAGS_core);
129 SetCurrentThreadRealtimePriority(FLAGS_receiver_priority);
130 epoll.Run();
131 UnsetCurrentThreadRealtimePriority();
132 epoll.DeleteFd(pipefd);
133
134 const chrono::nanoseconds average_latency = sum_latency / latency_count;
135
136 AOS_LOG(
137 INFO,
138 "Max named pip wakeup latency: %d.%03d microseconds, average: %d.%03d "
139 "microseconds\n",
140 static_cast<int>(max_wakeup_latency.count() / 1000),
141 static_cast<int>(max_wakeup_latency.count() % 1000),
142 static_cast<int>(average_latency.count() / 1000),
143 static_cast<int>(average_latency.count() % 1000));
144
145 PCHECK(close(pipefd));
146}
147
148int Main(int /*argc*/, char ** /*argv*/) {
149 mkfifo(FLAGS_fifo.c_str(), 0777);
150
151 AOS_LOG(INFO, "Main!\n");
152 ::std::thread t([]() {
153 TimerThread(monotonic_clock::now() + chrono::seconds(FLAGS_seconds),
154 FLAGS_timer_priority);
155 });
156
Austin Schuha0c41ba2020-09-10 22:59:14 -0700157 ::std::thread st([]() { SenderThread(); });
Austin Schuh5af45eb2019-09-16 20:54:18 -0700158
159 ReceiverThread();
160 st.join();
161
162 t.join();
163 return 0;
164}
165
166} // namespace aos
167
168int main(int argc, char **argv) {
169 ::gflags::ParseCommandLineFlags(&argc, &argv, true);
170
171 ::aos::logging::Init();
Austin Schuh5af45eb2019-09-16 20:54:18 -0700172
173 return ::aos::Main(argc, argv);
174}