blob: cd91ab35f2c1b611d6ab40e820d5c27dd4f01a90 [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
Austin Schuh6bae8252021-02-07 22:01:49 -080073auto CommonParameters() {
74 return ::testing::Combine(
75 ::testing::Values([]() { return new ShmEventLoopTestFactory(); }),
76 ::testing::Values(ReadMethod::COPY, ReadMethod::PIN),
77 ::testing::Values(DoTimingReports::kYes, DoTimingReports::kNo));
78}
Parker Schuhe4a70d62017-12-27 20:10:20 -080079
Austin Schuh6bae8252021-02-07 22:01:49 -080080INSTANTIATE_TEST_CASE_P(ShmEventLoopCommonTest, AbstractEventLoopTest,
81 CommonParameters());
Brian Silverman77162972020-08-12 19:52:40 -070082
Austin Schuh6bae8252021-02-07 22:01:49 -080083INSTANTIATE_TEST_CASE_P(ShmEventLoopCommonDeathTest, AbstractEventLoopDeathTest,
84 CommonParameters());
Austin Schuh6b6dfa52019-06-12 20:16:20 -070085
Parker Schuhe4a70d62017-12-27 20:10:20 -080086} // namespace
James Kuszmaulc79768b2019-02-18 15:08:44 -080087
Austin Schuh3115a202019-05-27 21:02:14 -070088bool IsRealtime() {
89 int scheduler;
Alex Perrycb7da4b2019-08-28 19:35:56 -070090 PCHECK((scheduler = sched_getscheduler(0)) != -1);
91
Austin Schuh62288252020-11-18 23:26:04 -080092 {
93 // If we are RT, logging the scheduler will crash us. Mark that we just
94 // don't care.
95 aos::ScopedNotRealtime nrt;
96 LOG(INFO) << "scheduler is " << scheduler;
97 }
98
99 const bool result = scheduler == SCHED_FIFO || scheduler == SCHED_RR;
100 // Confirm that the scheduler matches AOS' interpretation of if we are
101 // realtime or not.
102 if (result) {
103 aos::CheckRealtime();
104 } else {
105 aos::CheckNotRealtime();
106 }
107 return result;
Austin Schuh3115a202019-05-27 21:02:14 -0700108}
James Kuszmaulc79768b2019-02-18 15:08:44 -0800109
Brian Silvermana5450a92020-08-12 19:59:57 -0700110class ShmEventLoopTest : public ::testing::TestWithParam<ReadMethod> {
111 public:
112 ShmEventLoopTest() {
113 if (GetParam() == ReadMethod::PIN) {
114 factory_.PinReads();
115 }
116 }
117
118 ShmEventLoopTestFactory *factory() { return &factory_; }
119
120 private:
121 ShmEventLoopTestFactory factory_;
122};
123
124using ShmEventLoopDeathTest = ShmEventLoopTest;
125
Austin Schuh3115a202019-05-27 21:02:14 -0700126// Tests that every handler type is realtime and runs. There are threads
127// involved and it's easy to miss one.
Brian Silvermana5450a92020-08-12 19:59:57 -0700128TEST_P(ShmEventLoopTest, AllHandlersAreRealtime) {
129 auto loop = factory()->MakePrimary("primary");
130 auto loop2 = factory()->Make("loop2");
Austin Schuh3115a202019-05-27 21:02:14 -0700131
132 loop->SetRuntimeRealtimePriority(1);
133
134 auto sender = loop2->MakeSender<TestMessage>("/test");
135
136 bool did_onrun = false;
137 bool did_timer = false;
138 bool did_watcher = false;
139
Brian Silvermana5450a92020-08-12 19:59:57 -0700140 auto timer = loop->AddTimer([this, &did_timer]() {
Austin Schuh3115a202019-05-27 21:02:14 -0700141 EXPECT_TRUE(IsRealtime());
142 did_timer = true;
Brian Silvermana5450a92020-08-12 19:59:57 -0700143 factory()->Exit();
Austin Schuh3115a202019-05-27 21:02:14 -0700144 });
145
146 loop->MakeWatcher("/test", [&did_watcher](const TestMessage &) {
147 EXPECT_TRUE(IsRealtime());
148 did_watcher = true;
149 });
150
151 loop->OnRun([&loop, &did_onrun, &sender, timer]() {
152 EXPECT_TRUE(IsRealtime());
153 did_onrun = true;
154 timer->Setup(loop->monotonic_now() + chrono::milliseconds(100));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700155
156 aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
157 TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
158 builder.add_value(200);
159 msg.Send(builder.Finish());
Austin Schuh3115a202019-05-27 21:02:14 -0700160 });
James Kuszmaulc79768b2019-02-18 15:08:44 -0800161
Brian Silvermana5450a92020-08-12 19:59:57 -0700162 factory()->Run();
James Kuszmaulc79768b2019-02-18 15:08:44 -0800163
Austin Schuh3115a202019-05-27 21:02:14 -0700164 EXPECT_TRUE(did_onrun);
165 EXPECT_TRUE(did_timer);
166 EXPECT_TRUE(did_watcher);
James Kuszmaulc79768b2019-02-18 15:08:44 -0800167}
Austin Schuh52d325c2019-06-23 18:59:06 -0700168
169// Tests that missing a deadline inside the function still results in PhasedLoop
170// running at the right offset.
Brian Silvermana5450a92020-08-12 19:59:57 -0700171TEST_P(ShmEventLoopTest, DelayedPhasedLoop) {
172 auto loop1 = factory()->MakePrimary("primary");
Austin Schuh52d325c2019-06-23 18:59:06 -0700173
174 ::std::vector<::aos::monotonic_clock::time_point> times;
175
176 constexpr chrono::milliseconds kOffset = chrono::milliseconds(400);
177
178 loop1->AddPhasedLoop(
Brian Silvermana5450a92020-08-12 19:59:57 -0700179 [this, &times, &loop1, &kOffset](int count) {
Austin Schuh52d325c2019-06-23 18:59:06 -0700180 const ::aos::monotonic_clock::time_point monotonic_now =
181 loop1->monotonic_now();
182
183 // Compute our offset.
184 const ::aos::monotonic_clock::duration remainder =
185 monotonic_now.time_since_epoch() -
186 chrono::duration_cast<chrono::seconds>(
187 monotonic_now.time_since_epoch());
188
189 // Make sure we we are called near where we should be even when we
190 // delay.
191 constexpr chrono::milliseconds kEpsilon(200);
192 EXPECT_LT(remainder, kOffset + kEpsilon);
193 EXPECT_GT(remainder, kOffset - kEpsilon);
194
195 // Confirm that we see the missed count when we sleep.
196 if (times.size() == 0) {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800197 CHECK_EQ(count, 1);
Austin Schuh52d325c2019-06-23 18:59:06 -0700198 } else {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800199 CHECK_EQ(count, 3);
Austin Schuh52d325c2019-06-23 18:59:06 -0700200 }
201
202 times.push_back(loop1->monotonic_now());
203 if (times.size() == 2) {
Brian Silvermana5450a92020-08-12 19:59:57 -0700204 factory()->Exit();
Austin Schuh52d325c2019-06-23 18:59:06 -0700205 }
206
207 // Now, add a large delay. This should push us up to 3 cycles.
208 ::std::this_thread::sleep_for(chrono::milliseconds(2500));
209 },
210 chrono::seconds(1), kOffset);
211
Brian Silvermana5450a92020-08-12 19:59:57 -0700212 factory()->Run();
Austin Schuh52d325c2019-06-23 18:59:06 -0700213
214 EXPECT_EQ(times.size(), 2u);
215}
216
Brian Silverman5120afb2020-01-31 17:44:35 -0800217// Test GetWatcherSharedMemory in a few basic scenarios.
Brian Silvermana5450a92020-08-12 19:59:57 -0700218TEST_P(ShmEventLoopDeathTest, GetWatcherSharedMemory) {
219 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman5120afb2020-01-31 17:44:35 -0800220 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
221 const auto channel = configuration::GetChannel(
222 loop1->configuration(), "/test", TestMessage::GetFullyQualifiedName(),
223 loop1->name(), loop1->node());
224
225 // First verify it handles an invalid channel reasonably.
226 EXPECT_DEATH(loop1->GetWatcherSharedMemory(channel),
227 "No watcher found for channel");
228
229 // Then, actually create a watcher, and verify it returns something sane.
Brian Silvermana5450a92020-08-12 19:59:57 -0700230 absl::Span<const char> shared_memory;
231 bool ran = false;
232 loop1->MakeWatcher("/test", [this, &shared_memory,
233 &ran](const TestMessage &message) {
234 EXPECT_FALSE(ran);
235 ran = true;
236 // If we're using pinning, then we can verify that the message is actually
237 // in the specified region.
238 if (GetParam() == ReadMethod::PIN) {
239 EXPECT_GE(reinterpret_cast<const char *>(&message),
240 shared_memory.begin());
241 EXPECT_LT(reinterpret_cast<const char *>(&message), shared_memory.end());
242 }
243 factory()->Exit();
244 });
245 shared_memory = loop1->GetWatcherSharedMemory(channel);
246 EXPECT_FALSE(shared_memory.empty());
247
248 auto loop2 = factory()->Make("sender");
249 auto sender = loop2->MakeSender<TestMessage>("/test");
250 generic_loop1->OnRun([&sender]() {
251 auto builder = sender.MakeBuilder();
252 TestMessage::Builder test_builder(*builder.fbb());
253 test_builder.add_value(1);
254 CHECK(builder.Send(test_builder.Finish()));
255 });
256 factory()->Run();
257 EXPECT_TRUE(ran);
Brian Silverman5120afb2020-01-31 17:44:35 -0800258}
259
Brian Silvermana5450a92020-08-12 19:59:57 -0700260TEST_P(ShmEventLoopTest, GetSenderSharedMemory) {
261 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman5120afb2020-01-31 17:44:35 -0800262 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
263
Brian Silvermana5450a92020-08-12 19:59:57 -0700264 // Check that GetSenderSharedMemory returns non-null/non-empty memory span.
Brian Silverman5120afb2020-01-31 17:44:35 -0800265 auto sender = loop1->MakeSender<TestMessage>("/test");
Brian Silvermana5450a92020-08-12 19:59:57 -0700266 const absl::Span<char> shared_memory = loop1->GetSenderSharedMemory(&sender);
267 EXPECT_FALSE(shared_memory.empty());
268
269 auto builder = sender.MakeBuilder();
270 uint8_t *buffer;
271 builder.fbb()->CreateUninitializedVector(5, 1, &buffer);
272 EXPECT_GE(reinterpret_cast<char *>(buffer), shared_memory.begin());
273 EXPECT_LT(reinterpret_cast<char *>(buffer), shared_memory.end());
Brian Silverman5120afb2020-01-31 17:44:35 -0800274}
275
Brian Silvermana5450a92020-08-12 19:59:57 -0700276TEST_P(ShmEventLoopTest, GetFetcherPrivateMemory) {
277 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman6d2b3592020-06-18 14:40:15 -0700278 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
279
Brian Silvermana5450a92020-08-12 19:59:57 -0700280 // Check that GetFetcherPrivateMemory returns non-null/non-empty memory span.
Brian Silverman6d2b3592020-06-18 14:40:15 -0700281 auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
Brian Silvermana5450a92020-08-12 19:59:57 -0700282 const auto private_memory = loop1->GetFetcherPrivateMemory(&fetcher);
283 EXPECT_FALSE(private_memory.empty());
284
285 auto loop2 = factory()->Make("sender");
286 auto sender = loop2->MakeSender<TestMessage>("/test");
287 {
288 auto builder = sender.MakeBuilder();
289 TestMessage::Builder test_builder(*builder.fbb());
290 test_builder.add_value(1);
291 CHECK(builder.Send(test_builder.Finish()));
292 }
293
294 ASSERT_TRUE(fetcher.Fetch());
295 EXPECT_GE(fetcher.context().data, private_memory.begin());
296 EXPECT_LT(fetcher.context().data, private_memory.end());
Brian Silverman6d2b3592020-06-18 14:40:15 -0700297}
298
Brian Silverman0eaa1da2020-08-12 20:03:52 -0700299// Tests that corrupting the bytes around the data buffer results in a crash.
300TEST_P(ShmEventLoopDeathTest, OutOfBoundsWrite) {
301 auto loop1 = factory()->Make("loop1");
302 std::unique_ptr<aos::RawSender> sender =
303 loop1->MakeRawSender(configuration::GetChannel(
304 loop1->configuration(), "/test", "aos.TestMessage", "", nullptr));
305 for (size_t i = 0; i < kChannelDataRedzone; ++i) {
306 SCOPED_TRACE(std::to_string(i));
307 EXPECT_DEATH(
308 {
309 ++static_cast<char *>(sender->data())[-1 - i];
310 sender->Send(0);
311 },
312 "Somebody wrote outside the buffer of their message");
313 EXPECT_DEATH(
314 {
315 ++static_cast<char *>(sender->data())[sender->size() + i];
316 sender->Send(0);
317 },
318 "Somebody wrote outside the buffer of their message");
319 }
320}
321
Austin Schuh39788ff2019-12-01 18:22:57 -0800322// TODO(austin): Test that missing a deadline with a timer recovers as expected.
323
Brian Silvermana5450a92020-08-12 19:59:57 -0700324INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyTest, ShmEventLoopTest,
325 ::testing::Values(ReadMethod::COPY));
326INSTANTIATE_TEST_CASE_P(ShmEventLoopPinTest, ShmEventLoopTest,
327 ::testing::Values(ReadMethod::PIN));
328INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyDeathTest, ShmEventLoopDeathTest,
329 ::testing::Values(ReadMethod::COPY));
330INSTANTIATE_TEST_CASE_P(ShmEventLoopPinDeathTest, ShmEventLoopDeathTest,
331 ::testing::Values(ReadMethod::PIN));
332
Parker Schuhe4a70d62017-12-27 20:10:20 -0800333} // namespace testing
334} // namespace aos