blob: 8e7ad933efe1f071e5b800fdb1754d040174b9ee [file] [log] [blame]
Alex Perrycb7da4b2019-08-28 19:35:56 -07001#include "aos/events/shm_event_loop.h"
Parker Schuhe4a70d62017-12-27 20:10:20 -08002
Austin Schuh5f1cc5c2019-12-01 18:01:11 -08003#include <string_view>
4
Alex Perrycb7da4b2019-08-28 19:35:56 -07005#include "aos/events/event_loop_param_test.h"
Austin Schuh62288252020-11-18 23:26:04 -08006#include "aos/realtime.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07007#include "glog/logging.h"
Parker Schuhe4a70d62017-12-27 20:10:20 -08008#include "gtest/gtest.h"
9
Alex Perrycb7da4b2019-08-28 19:35:56 -070010#include "aos/events/test_message_generated.h"
Jim Ostrowski2192ddb2020-06-24 19:07:31 -070011#include "aos/network/team_number.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070012
Parker Schuhe4a70d62017-12-27 20:10:20 -080013namespace aos {
14namespace testing {
15namespace {
Austin Schuh3115a202019-05-27 21:02:14 -070016namespace chrono = ::std::chrono;
Parker Schuhe4a70d62017-12-27 20:10:20 -080017
18class ShmEventLoopTestFactory : public EventLoopTestFactory {
19 public:
Alex Perrycb7da4b2019-08-28 19:35:56 -070020 ShmEventLoopTestFactory() {
21 // Put all the queue files in ${TEST_TMPDIR} if it is set, otherwise
22 // everything will be reusing /dev/shm when sharded.
23 char *test_tmpdir = getenv("TEST_TMPDIR");
24 if (test_tmpdir != nullptr) {
25 FLAGS_shm_base = std::string(test_tmpdir) + "/aos";
26 }
27
28 // Clean up anything left there before.
Brian Silverman177567e2020-08-12 19:51:33 -070029 unlink((FLAGS_shm_base + "/test/aos.TestMessage.v3").c_str());
30 unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v3").c_str());
31 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v3").c_str());
32 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v3").c_str());
33 unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v3").c_str());
34 unlink((FLAGS_shm_base + "/aos/aos.logging.LogMessageFbs.v3").c_str());
Alex Perrycb7da4b2019-08-28 19:35:56 -070035 }
36
Austin Schuh217a9782019-12-21 23:02:50 -080037 ~ShmEventLoopTestFactory() { FLAGS_override_hostname = ""; }
38
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080039 ::std::unique_ptr<EventLoop> Make(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080040 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080041 FLAGS_override_hostname =
42 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080043 }
44 ::std::unique_ptr<ShmEventLoop> loop(new ShmEventLoop(configuration()));
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080045 loop->set_name(name);
Austin Schuh217a9782019-12-21 23:02:50 -080046 return std::move(loop);
Parker Schuhe4a70d62017-12-27 20:10:20 -080047 }
48
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080049 ::std::unique_ptr<EventLoop> MakePrimary(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080050 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080051 FLAGS_override_hostname =
52 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080053 }
Austin Schuh44019f92019-05-19 19:58:27 -070054 ::std::unique_ptr<ShmEventLoop> loop =
Alex Perrycb7da4b2019-08-28 19:35:56 -070055 ::std::unique_ptr<ShmEventLoop>(new ShmEventLoop(configuration()));
Austin Schuh44019f92019-05-19 19:58:27 -070056 primary_event_loop_ = loop.get();
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080057 loop->set_name(name);
58 return std::move(loop);
Austin Schuh44019f92019-05-19 19:58:27 -070059 }
60
Alex Perrycb7da4b2019-08-28 19:35:56 -070061 void Run() override { CHECK_NOTNULL(primary_event_loop_)->Run(); }
Austin Schuh44019f92019-05-19 19:58:27 -070062
Alex Perrycb7da4b2019-08-28 19:35:56 -070063 void Exit() override { CHECK_NOTNULL(primary_event_loop_)->Exit(); }
Austin Schuh9fe68f72019-08-10 19:32:03 -070064
Austin Schuh52d325c2019-06-23 18:59:06 -070065 void SleepFor(::std::chrono::nanoseconds duration) override {
66 ::std::this_thread::sleep_for(duration);
67 }
68
Austin Schuh44019f92019-05-19 19:58:27 -070069 private:
Austin Schuh44019f92019-05-19 19:58:27 -070070 ::aos::ShmEventLoop *primary_event_loop_;
Parker Schuhe4a70d62017-12-27 20:10:20 -080071};
72
Brian Silverman77162972020-08-12 19:52:40 -070073INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyTest, AbstractEventLoopTest,
74 ::testing::Values(std::make_pair(
75 []() { return new ShmEventLoopTestFactory(); },
76 ReadMethod::COPY)));
Parker Schuhe4a70d62017-12-27 20:10:20 -080077
Brian Silverman77162972020-08-12 19:52:40 -070078INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyDeathTest, AbstractEventLoopDeathTest,
79 ::testing::Values(std::make_pair(
80 []() { return new ShmEventLoopTestFactory(); },
81 ReadMethod::COPY)));
82
83INSTANTIATE_TEST_CASE_P(ShmEventLoopPinTest, AbstractEventLoopTest,
84 ::testing::Values(std::make_pair(
85 []() { return new ShmEventLoopTestFactory(); },
86 ReadMethod::PIN)));
87
88INSTANTIATE_TEST_CASE_P(ShmEventLoopPinDeathTest, AbstractEventLoopDeathTest,
89 ::testing::Values(std::make_pair(
90 []() { return new ShmEventLoopTestFactory(); },
91 ReadMethod::PIN)));
Austin Schuh6b6dfa52019-06-12 20:16:20 -070092
Parker Schuhe4a70d62017-12-27 20:10:20 -080093} // namespace
James Kuszmaulc79768b2019-02-18 15:08:44 -080094
Austin Schuh3115a202019-05-27 21:02:14 -070095bool IsRealtime() {
96 int scheduler;
Alex Perrycb7da4b2019-08-28 19:35:56 -070097 PCHECK((scheduler = sched_getscheduler(0)) != -1);
98
Austin Schuh62288252020-11-18 23:26:04 -080099 {
100 // If we are RT, logging the scheduler will crash us. Mark that we just
101 // don't care.
102 aos::ScopedNotRealtime nrt;
103 LOG(INFO) << "scheduler is " << scheduler;
104 }
105
106 const bool result = scheduler == SCHED_FIFO || scheduler == SCHED_RR;
107 // Confirm that the scheduler matches AOS' interpretation of if we are
108 // realtime or not.
109 if (result) {
110 aos::CheckRealtime();
111 } else {
112 aos::CheckNotRealtime();
113 }
114 return result;
Austin Schuh3115a202019-05-27 21:02:14 -0700115}
James Kuszmaulc79768b2019-02-18 15:08:44 -0800116
Brian Silvermana5450a92020-08-12 19:59:57 -0700117class ShmEventLoopTest : public ::testing::TestWithParam<ReadMethod> {
118 public:
119 ShmEventLoopTest() {
120 if (GetParam() == ReadMethod::PIN) {
121 factory_.PinReads();
122 }
123 }
124
125 ShmEventLoopTestFactory *factory() { return &factory_; }
126
127 private:
128 ShmEventLoopTestFactory factory_;
129};
130
131using ShmEventLoopDeathTest = ShmEventLoopTest;
132
Austin Schuh3115a202019-05-27 21:02:14 -0700133// Tests that every handler type is realtime and runs. There are threads
134// involved and it's easy to miss one.
Brian Silvermana5450a92020-08-12 19:59:57 -0700135TEST_P(ShmEventLoopTest, AllHandlersAreRealtime) {
136 auto loop = factory()->MakePrimary("primary");
137 auto loop2 = factory()->Make("loop2");
Austin Schuh3115a202019-05-27 21:02:14 -0700138
139 loop->SetRuntimeRealtimePriority(1);
140
141 auto sender = loop2->MakeSender<TestMessage>("/test");
142
143 bool did_onrun = false;
144 bool did_timer = false;
145 bool did_watcher = false;
146
Brian Silvermana5450a92020-08-12 19:59:57 -0700147 auto timer = loop->AddTimer([this, &did_timer]() {
Austin Schuh3115a202019-05-27 21:02:14 -0700148 EXPECT_TRUE(IsRealtime());
149 did_timer = true;
Brian Silvermana5450a92020-08-12 19:59:57 -0700150 factory()->Exit();
Austin Schuh3115a202019-05-27 21:02:14 -0700151 });
152
153 loop->MakeWatcher("/test", [&did_watcher](const TestMessage &) {
154 EXPECT_TRUE(IsRealtime());
155 did_watcher = true;
156 });
157
158 loop->OnRun([&loop, &did_onrun, &sender, timer]() {
159 EXPECT_TRUE(IsRealtime());
160 did_onrun = true;
161 timer->Setup(loop->monotonic_now() + chrono::milliseconds(100));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700162
163 aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
164 TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
165 builder.add_value(200);
166 msg.Send(builder.Finish());
Austin Schuh3115a202019-05-27 21:02:14 -0700167 });
James Kuszmaulc79768b2019-02-18 15:08:44 -0800168
Brian Silvermana5450a92020-08-12 19:59:57 -0700169 factory()->Run();
James Kuszmaulc79768b2019-02-18 15:08:44 -0800170
Austin Schuh3115a202019-05-27 21:02:14 -0700171 EXPECT_TRUE(did_onrun);
172 EXPECT_TRUE(did_timer);
173 EXPECT_TRUE(did_watcher);
James Kuszmaulc79768b2019-02-18 15:08:44 -0800174}
Austin Schuh52d325c2019-06-23 18:59:06 -0700175
176// Tests that missing a deadline inside the function still results in PhasedLoop
177// running at the right offset.
Brian Silvermana5450a92020-08-12 19:59:57 -0700178TEST_P(ShmEventLoopTest, DelayedPhasedLoop) {
179 auto loop1 = factory()->MakePrimary("primary");
Austin Schuh52d325c2019-06-23 18:59:06 -0700180
181 ::std::vector<::aos::monotonic_clock::time_point> times;
182
183 constexpr chrono::milliseconds kOffset = chrono::milliseconds(400);
184
185 loop1->AddPhasedLoop(
Brian Silvermana5450a92020-08-12 19:59:57 -0700186 [this, &times, &loop1, &kOffset](int count) {
Austin Schuh52d325c2019-06-23 18:59:06 -0700187 const ::aos::monotonic_clock::time_point monotonic_now =
188 loop1->monotonic_now();
189
190 // Compute our offset.
191 const ::aos::monotonic_clock::duration remainder =
192 monotonic_now.time_since_epoch() -
193 chrono::duration_cast<chrono::seconds>(
194 monotonic_now.time_since_epoch());
195
196 // Make sure we we are called near where we should be even when we
197 // delay.
198 constexpr chrono::milliseconds kEpsilon(200);
199 EXPECT_LT(remainder, kOffset + kEpsilon);
200 EXPECT_GT(remainder, kOffset - kEpsilon);
201
202 // Confirm that we see the missed count when we sleep.
203 if (times.size() == 0) {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800204 CHECK_EQ(count, 1);
Austin Schuh52d325c2019-06-23 18:59:06 -0700205 } else {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800206 CHECK_EQ(count, 3);
Austin Schuh52d325c2019-06-23 18:59:06 -0700207 }
208
209 times.push_back(loop1->monotonic_now());
210 if (times.size() == 2) {
Brian Silvermana5450a92020-08-12 19:59:57 -0700211 factory()->Exit();
Austin Schuh52d325c2019-06-23 18:59:06 -0700212 }
213
214 // Now, add a large delay. This should push us up to 3 cycles.
215 ::std::this_thread::sleep_for(chrono::milliseconds(2500));
216 },
217 chrono::seconds(1), kOffset);
218
Brian Silvermana5450a92020-08-12 19:59:57 -0700219 factory()->Run();
Austin Schuh52d325c2019-06-23 18:59:06 -0700220
221 EXPECT_EQ(times.size(), 2u);
222}
223
Brian Silverman5120afb2020-01-31 17:44:35 -0800224// Test GetWatcherSharedMemory in a few basic scenarios.
Brian Silvermana5450a92020-08-12 19:59:57 -0700225TEST_P(ShmEventLoopDeathTest, GetWatcherSharedMemory) {
226 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman5120afb2020-01-31 17:44:35 -0800227 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
228 const auto channel = configuration::GetChannel(
229 loop1->configuration(), "/test", TestMessage::GetFullyQualifiedName(),
230 loop1->name(), loop1->node());
231
232 // First verify it handles an invalid channel reasonably.
233 EXPECT_DEATH(loop1->GetWatcherSharedMemory(channel),
234 "No watcher found for channel");
235
236 // Then, actually create a watcher, and verify it returns something sane.
Brian Silvermana5450a92020-08-12 19:59:57 -0700237 absl::Span<const char> shared_memory;
238 bool ran = false;
239 loop1->MakeWatcher("/test", [this, &shared_memory,
240 &ran](const TestMessage &message) {
241 EXPECT_FALSE(ran);
242 ran = true;
243 // If we're using pinning, then we can verify that the message is actually
244 // in the specified region.
245 if (GetParam() == ReadMethod::PIN) {
246 EXPECT_GE(reinterpret_cast<const char *>(&message),
247 shared_memory.begin());
248 EXPECT_LT(reinterpret_cast<const char *>(&message), shared_memory.end());
249 }
250 factory()->Exit();
251 });
252 shared_memory = loop1->GetWatcherSharedMemory(channel);
253 EXPECT_FALSE(shared_memory.empty());
254
255 auto loop2 = factory()->Make("sender");
256 auto sender = loop2->MakeSender<TestMessage>("/test");
257 generic_loop1->OnRun([&sender]() {
258 auto builder = sender.MakeBuilder();
259 TestMessage::Builder test_builder(*builder.fbb());
260 test_builder.add_value(1);
261 CHECK(builder.Send(test_builder.Finish()));
262 });
263 factory()->Run();
264 EXPECT_TRUE(ran);
Brian Silverman5120afb2020-01-31 17:44:35 -0800265}
266
Brian Silvermana5450a92020-08-12 19:59:57 -0700267TEST_P(ShmEventLoopTest, GetSenderSharedMemory) {
268 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman5120afb2020-01-31 17:44:35 -0800269 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
270
Brian Silvermana5450a92020-08-12 19:59:57 -0700271 // Check that GetSenderSharedMemory returns non-null/non-empty memory span.
Brian Silverman5120afb2020-01-31 17:44:35 -0800272 auto sender = loop1->MakeSender<TestMessage>("/test");
Brian Silvermana5450a92020-08-12 19:59:57 -0700273 const absl::Span<char> shared_memory = loop1->GetSenderSharedMemory(&sender);
274 EXPECT_FALSE(shared_memory.empty());
275
276 auto builder = sender.MakeBuilder();
277 uint8_t *buffer;
278 builder.fbb()->CreateUninitializedVector(5, 1, &buffer);
279 EXPECT_GE(reinterpret_cast<char *>(buffer), shared_memory.begin());
280 EXPECT_LT(reinterpret_cast<char *>(buffer), shared_memory.end());
Brian Silverman5120afb2020-01-31 17:44:35 -0800281}
282
Brian Silvermana5450a92020-08-12 19:59:57 -0700283TEST_P(ShmEventLoopTest, GetFetcherPrivateMemory) {
284 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman6d2b3592020-06-18 14:40:15 -0700285 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
286
Brian Silvermana5450a92020-08-12 19:59:57 -0700287 // Check that GetFetcherPrivateMemory returns non-null/non-empty memory span.
Brian Silverman6d2b3592020-06-18 14:40:15 -0700288 auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
Brian Silvermana5450a92020-08-12 19:59:57 -0700289 const auto private_memory = loop1->GetFetcherPrivateMemory(&fetcher);
290 EXPECT_FALSE(private_memory.empty());
291
292 auto loop2 = factory()->Make("sender");
293 auto sender = loop2->MakeSender<TestMessage>("/test");
294 {
295 auto builder = sender.MakeBuilder();
296 TestMessage::Builder test_builder(*builder.fbb());
297 test_builder.add_value(1);
298 CHECK(builder.Send(test_builder.Finish()));
299 }
300
301 ASSERT_TRUE(fetcher.Fetch());
302 EXPECT_GE(fetcher.context().data, private_memory.begin());
303 EXPECT_LT(fetcher.context().data, private_memory.end());
Brian Silverman6d2b3592020-06-18 14:40:15 -0700304}
305
Brian Silverman0eaa1da2020-08-12 20:03:52 -0700306// Tests that corrupting the bytes around the data buffer results in a crash.
307TEST_P(ShmEventLoopDeathTest, OutOfBoundsWrite) {
308 auto loop1 = factory()->Make("loop1");
309 std::unique_ptr<aos::RawSender> sender =
310 loop1->MakeRawSender(configuration::GetChannel(
311 loop1->configuration(), "/test", "aos.TestMessage", "", nullptr));
312 for (size_t i = 0; i < kChannelDataRedzone; ++i) {
313 SCOPED_TRACE(std::to_string(i));
314 EXPECT_DEATH(
315 {
316 ++static_cast<char *>(sender->data())[-1 - i];
317 sender->Send(0);
318 },
319 "Somebody wrote outside the buffer of their message");
320 EXPECT_DEATH(
321 {
322 ++static_cast<char *>(sender->data())[sender->size() + i];
323 sender->Send(0);
324 },
325 "Somebody wrote outside the buffer of their message");
326 }
327}
328
Austin Schuh39788ff2019-12-01 18:22:57 -0800329// TODO(austin): Test that missing a deadline with a timer recovers as expected.
330
Brian Silvermana5450a92020-08-12 19:59:57 -0700331INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyTest, ShmEventLoopTest,
332 ::testing::Values(ReadMethod::COPY));
333INSTANTIATE_TEST_CASE_P(ShmEventLoopPinTest, ShmEventLoopTest,
334 ::testing::Values(ReadMethod::PIN));
335INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyDeathTest, ShmEventLoopDeathTest,
336 ::testing::Values(ReadMethod::COPY));
337INSTANTIATE_TEST_CASE_P(ShmEventLoopPinDeathTest, ShmEventLoopDeathTest,
338 ::testing::Values(ReadMethod::PIN));
339
Parker Schuhe4a70d62017-12-27 20:10:20 -0800340} // namespace testing
341} // namespace aos