blob: 3d609f87977212e4e9bb9325a7008114bdd9dbad [file] [log] [blame]
Austin Schuh5af45eb2019-09-16 20:54:18 -07001#include "gflags/gflags.h"
2
3#include <fcntl.h>
4#include <sys/stat.h>
5#include <sys/types.h>
6#include <chrono>
7#include <random>
8#include <thread>
9
10#include "aos/condition.h"
11#include "aos/init.h"
12#include "aos/ipc_lib/latency_lib.h"
13#include "aos/logging/implementations.h"
14#include "aos/logging/logging.h"
15#include "aos/mutex/mutex.h"
16#include "aos/realtime.h"
17#include "aos/time/time.h"
18
19DEFINE_int32(seconds, 10, "Duration of the test to run");
20DEFINE_int32(
21 latency_threshold, 1000,
22 "Disable tracing when anything takes more than this many microseoncds");
23DEFINE_int32(core, 7, "Core to pin to");
24DEFINE_int32(sender_priority, 53, "RT priority to send at");
25DEFINE_int32(receiver_priority, 52, "RT priority to receive at");
26DEFINE_int32(timer_priority, 51, "RT priority to spin the timer at");
27
28DEFINE_bool(log_latency, false, "If true, log the latency");
29
30const uint32_t kSignalNumber = SIGRTMIN + 1;
31const uint32_t kQuitSignalNumber = SIGRTMIN + 2;
32
33namespace chrono = ::std::chrono;
34
35namespace aos {
36
37struct WakeupData {
38 Mutex mutex;
39 Condition condition;
40
41 WakeupData() : condition(&mutex) {}
42
43 monotonic_clock::time_point wakeup_time = monotonic_clock::epoch();
44
45 bool done = false;
46};
47
48void SenderThread(WakeupData *data) {
49 const monotonic_clock::time_point end_time =
50 monotonic_clock::now() + chrono::seconds(FLAGS_seconds);
51 // Standard mersenne_twister_engine seeded with 0
52 ::std::mt19937 generator(0);
53
54 // Sleep between 1 and 15 ms.
55 ::std::uniform_int_distribution<> distribution(1000, 15000);
56
Austin Schuh094d09b2020-11-20 23:26:52 -080057 {
58 cpu_set_t cpuset;
59 CPU_ZERO(&cpuset);
60 CPU_SET(FLAGS_core, &cpuset);
61
62 SetCurrentThreadAffinity(cpuset);
63 }
Austin Schuh5af45eb2019-09-16 20:54:18 -070064 SetCurrentThreadRealtimePriority(FLAGS_sender_priority);
65 while (true) {
66 const monotonic_clock::time_point wakeup_time =
67 monotonic_clock::now() + chrono::microseconds(distribution(generator));
68
69 ::std::this_thread::sleep_until(wakeup_time);
70 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
71
72 {
73 MutexLocker locker(&data->mutex);
74 data->wakeup_time = monotonic_now;
75 data->condition.Broadcast();
76
77 if (monotonic_now > end_time) {
78 break;
79 }
80 }
81 }
82
83 {
84 MutexLocker locker(&data->mutex);
85 data->done = true;
86 data->condition.Broadcast();
87 }
88
89 UnsetCurrentThreadRealtimePriority();
90}
91
92void ReceiverThread(WakeupData *data) {
93 Tracing t;
94 t.Start();
95
96 chrono::nanoseconds max_wakeup_latency = chrono::nanoseconds(0);
97 chrono::nanoseconds sum_latency = chrono::nanoseconds(0);
98 int latency_count = 0;
99
Austin Schuh094d09b2020-11-20 23:26:52 -0800100 {
101 cpu_set_t cpuset;
102 CPU_ZERO(&cpuset);
103 CPU_SET(FLAGS_core, &cpuset);
104
105 SetCurrentThreadAffinity(cpuset);
106 }
Austin Schuh5af45eb2019-09-16 20:54:18 -0700107 SetCurrentThreadRealtimePriority(FLAGS_receiver_priority);
108 while (true) {
109 chrono::nanoseconds wakeup_latency;
110 {
111 MutexLocker locker(&data->mutex);
112 while (data->wakeup_time == monotonic_clock::epoch() && !data->done) {
113 CHECK(!data->condition.Wait());
114 }
115
116 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
117
118 if (data->done) {
119 break;
120 }
121
122 wakeup_latency = monotonic_now - data->wakeup_time;
123 data->wakeup_time = monotonic_clock::epoch();
124 }
125
126 sum_latency += wakeup_latency;
127 ++latency_count;
128
129 max_wakeup_latency = ::std::max(wakeup_latency, max_wakeup_latency);
130
131 if (wakeup_latency > chrono::microseconds(FLAGS_latency_threshold)) {
132 t.Stop();
133 AOS_LOG(INFO, "Stopped tracing, latency %" PRId64 "\n",
134 static_cast<int64_t>(wakeup_latency.count()));
135 }
136
137 if (FLAGS_log_latency) {
138 AOS_LOG(INFO, "dt: %8d.%03d\n",
139 static_cast<int>(wakeup_latency.count() / 1000),
140 static_cast<int>(wakeup_latency.count() % 1000));
141 }
142 }
143 UnsetCurrentThreadRealtimePriority();
144
145 const chrono::nanoseconds average_latency = sum_latency / latency_count;
146
147 AOS_LOG(INFO,
148 "Max futex wakeup latency: %d.%03d microseconds, average: %d.%03d "
149 "microseconds\n",
150 static_cast<int>(max_wakeup_latency.count() / 1000),
151 static_cast<int>(max_wakeup_latency.count() % 1000),
152 static_cast<int>(average_latency.count() / 1000),
153 static_cast<int>(average_latency.count() % 1000));
154}
155
156int Main(int /*argc*/, char ** /*argv*/) {
157 WakeupData data;
158
159 AOS_LOG(INFO, "Main!\n");
160 ::std::thread t([]() {
161 TimerThread(monotonic_clock::now() + chrono::seconds(FLAGS_seconds),
162 FLAGS_timer_priority);
163 });
164
165 ::std::thread st([&data]() { SenderThread(&data); });
166
167 ReceiverThread(&data);
168
169 st.join();
170 t.join();
171 return 0;
172}
173
174} // namespace aos
175
176int main(int argc, char **argv) {
177 ::gflags::ParseCommandLineFlags(&argc, &argv, true);
178
Austin Schuh5af45eb2019-09-16 20:54:18 -0700179 return ::aos::Main(argc, argv);
180}