blob: 2aeefb481d90736954fa7d982d1cc33553e7479f [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"
6#include "glog/logging.h"
Parker Schuhe4a70d62017-12-27 20:10:20 -08007#include "gtest/gtest.h"
8
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "aos/events/test_message_generated.h"
Jim Ostrowski2192ddb2020-06-24 19:07:31 -070010#include "aos/network/team_number.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070011
Parker Schuhe4a70d62017-12-27 20:10:20 -080012namespace aos {
13namespace testing {
14namespace {
Austin Schuh3115a202019-05-27 21:02:14 -070015namespace chrono = ::std::chrono;
Parker Schuhe4a70d62017-12-27 20:10:20 -080016
17class ShmEventLoopTestFactory : public EventLoopTestFactory {
18 public:
Alex Perrycb7da4b2019-08-28 19:35:56 -070019 ShmEventLoopTestFactory() {
20 // Put all the queue files in ${TEST_TMPDIR} if it is set, otherwise
21 // everything will be reusing /dev/shm when sharded.
22 char *test_tmpdir = getenv("TEST_TMPDIR");
23 if (test_tmpdir != nullptr) {
24 FLAGS_shm_base = std::string(test_tmpdir) + "/aos";
25 }
26
27 // Clean up anything left there before.
Brian Silverman177567e2020-08-12 19:51:33 -070028 unlink((FLAGS_shm_base + "/test/aos.TestMessage.v3").c_str());
29 unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v3").c_str());
30 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v3").c_str());
31 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v3").c_str());
32 unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v3").c_str());
33 unlink((FLAGS_shm_base + "/aos/aos.logging.LogMessageFbs.v3").c_str());
Alex Perrycb7da4b2019-08-28 19:35:56 -070034 }
35
Austin Schuh217a9782019-12-21 23:02:50 -080036 ~ShmEventLoopTestFactory() { FLAGS_override_hostname = ""; }
37
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080038 ::std::unique_ptr<EventLoop> Make(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080039 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080040 FLAGS_override_hostname =
41 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080042 }
43 ::std::unique_ptr<ShmEventLoop> loop(new ShmEventLoop(configuration()));
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080044 loop->set_name(name);
Austin Schuh217a9782019-12-21 23:02:50 -080045 return std::move(loop);
Parker Schuhe4a70d62017-12-27 20:10:20 -080046 }
47
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080048 ::std::unique_ptr<EventLoop> MakePrimary(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080049 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080050 FLAGS_override_hostname =
51 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080052 }
Austin Schuh44019f92019-05-19 19:58:27 -070053 ::std::unique_ptr<ShmEventLoop> loop =
Alex Perrycb7da4b2019-08-28 19:35:56 -070054 ::std::unique_ptr<ShmEventLoop>(new ShmEventLoop(configuration()));
Austin Schuh44019f92019-05-19 19:58:27 -070055 primary_event_loop_ = loop.get();
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080056 loop->set_name(name);
57 return std::move(loop);
Austin Schuh44019f92019-05-19 19:58:27 -070058 }
59
Alex Perrycb7da4b2019-08-28 19:35:56 -070060 void Run() override { CHECK_NOTNULL(primary_event_loop_)->Run(); }
Austin Schuh44019f92019-05-19 19:58:27 -070061
Alex Perrycb7da4b2019-08-28 19:35:56 -070062 void Exit() override { CHECK_NOTNULL(primary_event_loop_)->Exit(); }
Austin Schuh9fe68f72019-08-10 19:32:03 -070063
Austin Schuh52d325c2019-06-23 18:59:06 -070064 void SleepFor(::std::chrono::nanoseconds duration) override {
65 ::std::this_thread::sleep_for(duration);
66 }
67
Austin Schuh44019f92019-05-19 19:58:27 -070068 private:
Austin Schuh44019f92019-05-19 19:58:27 -070069 ::aos::ShmEventLoop *primary_event_loop_;
Parker Schuhe4a70d62017-12-27 20:10:20 -080070};
71
Brian Silverman77162972020-08-12 19:52:40 -070072INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyTest, AbstractEventLoopTest,
73 ::testing::Values(std::make_pair(
74 []() { return new ShmEventLoopTestFactory(); },
75 ReadMethod::COPY)));
Parker Schuhe4a70d62017-12-27 20:10:20 -080076
Brian Silverman77162972020-08-12 19:52:40 -070077INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyDeathTest, AbstractEventLoopDeathTest,
78 ::testing::Values(std::make_pair(
79 []() { return new ShmEventLoopTestFactory(); },
80 ReadMethod::COPY)));
81
82INSTANTIATE_TEST_CASE_P(ShmEventLoopPinTest, AbstractEventLoopTest,
83 ::testing::Values(std::make_pair(
84 []() { return new ShmEventLoopTestFactory(); },
85 ReadMethod::PIN)));
86
87INSTANTIATE_TEST_CASE_P(ShmEventLoopPinDeathTest, AbstractEventLoopDeathTest,
88 ::testing::Values(std::make_pair(
89 []() { return new ShmEventLoopTestFactory(); },
90 ReadMethod::PIN)));
Austin Schuh6b6dfa52019-06-12 20:16:20 -070091
Parker Schuhe4a70d62017-12-27 20:10:20 -080092} // namespace
James Kuszmaulc79768b2019-02-18 15:08:44 -080093
Austin Schuh3115a202019-05-27 21:02:14 -070094bool IsRealtime() {
95 int scheduler;
Alex Perrycb7da4b2019-08-28 19:35:56 -070096 PCHECK((scheduler = sched_getscheduler(0)) != -1);
97
98 LOG(INFO) << "scheduler is " << scheduler;
Austin Schuh3115a202019-05-27 21:02:14 -070099 return scheduler == SCHED_FIFO || scheduler == SCHED_RR;
100}
James Kuszmaulc79768b2019-02-18 15:08:44 -0800101
Brian Silvermana5450a92020-08-12 19:59:57 -0700102class ShmEventLoopTest : public ::testing::TestWithParam<ReadMethod> {
103 public:
104 ShmEventLoopTest() {
105 if (GetParam() == ReadMethod::PIN) {
106 factory_.PinReads();
107 }
108 }
109
110 ShmEventLoopTestFactory *factory() { return &factory_; }
111
112 private:
113 ShmEventLoopTestFactory factory_;
114};
115
116using ShmEventLoopDeathTest = ShmEventLoopTest;
117
Austin Schuh3115a202019-05-27 21:02:14 -0700118// Tests that every handler type is realtime and runs. There are threads
119// involved and it's easy to miss one.
Brian Silvermana5450a92020-08-12 19:59:57 -0700120TEST_P(ShmEventLoopTest, AllHandlersAreRealtime) {
121 auto loop = factory()->MakePrimary("primary");
122 auto loop2 = factory()->Make("loop2");
Austin Schuh3115a202019-05-27 21:02:14 -0700123
124 loop->SetRuntimeRealtimePriority(1);
125
126 auto sender = loop2->MakeSender<TestMessage>("/test");
127
128 bool did_onrun = false;
129 bool did_timer = false;
130 bool did_watcher = false;
131
Brian Silvermana5450a92020-08-12 19:59:57 -0700132 auto timer = loop->AddTimer([this, &did_timer]() {
Austin Schuh3115a202019-05-27 21:02:14 -0700133 EXPECT_TRUE(IsRealtime());
134 did_timer = true;
Brian Silvermana5450a92020-08-12 19:59:57 -0700135 factory()->Exit();
Austin Schuh3115a202019-05-27 21:02:14 -0700136 });
137
138 loop->MakeWatcher("/test", [&did_watcher](const TestMessage &) {
139 EXPECT_TRUE(IsRealtime());
140 did_watcher = true;
141 });
142
143 loop->OnRun([&loop, &did_onrun, &sender, timer]() {
144 EXPECT_TRUE(IsRealtime());
145 did_onrun = true;
146 timer->Setup(loop->monotonic_now() + chrono::milliseconds(100));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700147
148 aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
149 TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
150 builder.add_value(200);
151 msg.Send(builder.Finish());
Austin Schuh3115a202019-05-27 21:02:14 -0700152 });
James Kuszmaulc79768b2019-02-18 15:08:44 -0800153
Brian Silvermana5450a92020-08-12 19:59:57 -0700154 factory()->Run();
James Kuszmaulc79768b2019-02-18 15:08:44 -0800155
Austin Schuh3115a202019-05-27 21:02:14 -0700156 EXPECT_TRUE(did_onrun);
157 EXPECT_TRUE(did_timer);
158 EXPECT_TRUE(did_watcher);
James Kuszmaulc79768b2019-02-18 15:08:44 -0800159}
Austin Schuh52d325c2019-06-23 18:59:06 -0700160
161// Tests that missing a deadline inside the function still results in PhasedLoop
162// running at the right offset.
Brian Silvermana5450a92020-08-12 19:59:57 -0700163TEST_P(ShmEventLoopTest, DelayedPhasedLoop) {
164 auto loop1 = factory()->MakePrimary("primary");
Austin Schuh52d325c2019-06-23 18:59:06 -0700165
166 ::std::vector<::aos::monotonic_clock::time_point> times;
167
168 constexpr chrono::milliseconds kOffset = chrono::milliseconds(400);
169
170 loop1->AddPhasedLoop(
Brian Silvermana5450a92020-08-12 19:59:57 -0700171 [this, &times, &loop1, &kOffset](int count) {
Austin Schuh52d325c2019-06-23 18:59:06 -0700172 const ::aos::monotonic_clock::time_point monotonic_now =
173 loop1->monotonic_now();
174
175 // Compute our offset.
176 const ::aos::monotonic_clock::duration remainder =
177 monotonic_now.time_since_epoch() -
178 chrono::duration_cast<chrono::seconds>(
179 monotonic_now.time_since_epoch());
180
181 // Make sure we we are called near where we should be even when we
182 // delay.
183 constexpr chrono::milliseconds kEpsilon(200);
184 EXPECT_LT(remainder, kOffset + kEpsilon);
185 EXPECT_GT(remainder, kOffset - kEpsilon);
186
187 // Confirm that we see the missed count when we sleep.
188 if (times.size() == 0) {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800189 CHECK_EQ(count, 1);
Austin Schuh52d325c2019-06-23 18:59:06 -0700190 } else {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800191 CHECK_EQ(count, 3);
Austin Schuh52d325c2019-06-23 18:59:06 -0700192 }
193
194 times.push_back(loop1->monotonic_now());
195 if (times.size() == 2) {
Brian Silvermana5450a92020-08-12 19:59:57 -0700196 factory()->Exit();
Austin Schuh52d325c2019-06-23 18:59:06 -0700197 }
198
199 // Now, add a large delay. This should push us up to 3 cycles.
200 ::std::this_thread::sleep_for(chrono::milliseconds(2500));
201 },
202 chrono::seconds(1), kOffset);
203
Brian Silvermana5450a92020-08-12 19:59:57 -0700204 factory()->Run();
Austin Schuh52d325c2019-06-23 18:59:06 -0700205
206 EXPECT_EQ(times.size(), 2u);
207}
208
Brian Silverman5120afb2020-01-31 17:44:35 -0800209// Test GetWatcherSharedMemory in a few basic scenarios.
Brian Silvermana5450a92020-08-12 19:59:57 -0700210TEST_P(ShmEventLoopDeathTest, GetWatcherSharedMemory) {
211 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman5120afb2020-01-31 17:44:35 -0800212 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
213 const auto channel = configuration::GetChannel(
214 loop1->configuration(), "/test", TestMessage::GetFullyQualifiedName(),
215 loop1->name(), loop1->node());
216
217 // First verify it handles an invalid channel reasonably.
218 EXPECT_DEATH(loop1->GetWatcherSharedMemory(channel),
219 "No watcher found for channel");
220
221 // Then, actually create a watcher, and verify it returns something sane.
Brian Silvermana5450a92020-08-12 19:59:57 -0700222 absl::Span<const char> shared_memory;
223 bool ran = false;
224 loop1->MakeWatcher("/test", [this, &shared_memory,
225 &ran](const TestMessage &message) {
226 EXPECT_FALSE(ran);
227 ran = true;
228 // If we're using pinning, then we can verify that the message is actually
229 // in the specified region.
230 if (GetParam() == ReadMethod::PIN) {
231 EXPECT_GE(reinterpret_cast<const char *>(&message),
232 shared_memory.begin());
233 EXPECT_LT(reinterpret_cast<const char *>(&message), shared_memory.end());
234 }
235 factory()->Exit();
236 });
237 shared_memory = loop1->GetWatcherSharedMemory(channel);
238 EXPECT_FALSE(shared_memory.empty());
239
240 auto loop2 = factory()->Make("sender");
241 auto sender = loop2->MakeSender<TestMessage>("/test");
242 generic_loop1->OnRun([&sender]() {
243 auto builder = sender.MakeBuilder();
244 TestMessage::Builder test_builder(*builder.fbb());
245 test_builder.add_value(1);
246 CHECK(builder.Send(test_builder.Finish()));
247 });
248 factory()->Run();
249 EXPECT_TRUE(ran);
Brian Silverman5120afb2020-01-31 17:44:35 -0800250}
251
Brian Silvermana5450a92020-08-12 19:59:57 -0700252TEST_P(ShmEventLoopTest, GetSenderSharedMemory) {
253 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman5120afb2020-01-31 17:44:35 -0800254 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
255
Brian Silvermana5450a92020-08-12 19:59:57 -0700256 // Check that GetSenderSharedMemory returns non-null/non-empty memory span.
Brian Silverman5120afb2020-01-31 17:44:35 -0800257 auto sender = loop1->MakeSender<TestMessage>("/test");
Brian Silvermana5450a92020-08-12 19:59:57 -0700258 const absl::Span<char> shared_memory = loop1->GetSenderSharedMemory(&sender);
259 EXPECT_FALSE(shared_memory.empty());
260
261 auto builder = sender.MakeBuilder();
262 uint8_t *buffer;
263 builder.fbb()->CreateUninitializedVector(5, 1, &buffer);
264 EXPECT_GE(reinterpret_cast<char *>(buffer), shared_memory.begin());
265 EXPECT_LT(reinterpret_cast<char *>(buffer), shared_memory.end());
Brian Silverman5120afb2020-01-31 17:44:35 -0800266}
267
Brian Silvermana5450a92020-08-12 19:59:57 -0700268TEST_P(ShmEventLoopTest, GetFetcherPrivateMemory) {
269 auto generic_loop1 = factory()->MakePrimary("primary");
Brian Silverman6d2b3592020-06-18 14:40:15 -0700270 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
271
Brian Silvermana5450a92020-08-12 19:59:57 -0700272 // Check that GetFetcherPrivateMemory returns non-null/non-empty memory span.
Brian Silverman6d2b3592020-06-18 14:40:15 -0700273 auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
Brian Silvermana5450a92020-08-12 19:59:57 -0700274 const auto private_memory = loop1->GetFetcherPrivateMemory(&fetcher);
275 EXPECT_FALSE(private_memory.empty());
276
277 auto loop2 = factory()->Make("sender");
278 auto sender = loop2->MakeSender<TestMessage>("/test");
279 {
280 auto builder = sender.MakeBuilder();
281 TestMessage::Builder test_builder(*builder.fbb());
282 test_builder.add_value(1);
283 CHECK(builder.Send(test_builder.Finish()));
284 }
285
286 ASSERT_TRUE(fetcher.Fetch());
287 EXPECT_GE(fetcher.context().data, private_memory.begin());
288 EXPECT_LT(fetcher.context().data, private_memory.end());
Brian Silverman6d2b3592020-06-18 14:40:15 -0700289}
290
Brian Silverman0eaa1da2020-08-12 20:03:52 -0700291// Tests that corrupting the bytes around the data buffer results in a crash.
292TEST_P(ShmEventLoopDeathTest, OutOfBoundsWrite) {
293 auto loop1 = factory()->Make("loop1");
294 std::unique_ptr<aos::RawSender> sender =
295 loop1->MakeRawSender(configuration::GetChannel(
296 loop1->configuration(), "/test", "aos.TestMessage", "", nullptr));
297 for (size_t i = 0; i < kChannelDataRedzone; ++i) {
298 SCOPED_TRACE(std::to_string(i));
299 EXPECT_DEATH(
300 {
301 ++static_cast<char *>(sender->data())[-1 - i];
302 sender->Send(0);
303 },
304 "Somebody wrote outside the buffer of their message");
305 EXPECT_DEATH(
306 {
307 ++static_cast<char *>(sender->data())[sender->size() + i];
308 sender->Send(0);
309 },
310 "Somebody wrote outside the buffer of their message");
311 }
312}
313
Austin Schuh39788ff2019-12-01 18:22:57 -0800314// TODO(austin): Test that missing a deadline with a timer recovers as expected.
315
Brian Silvermana5450a92020-08-12 19:59:57 -0700316INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyTest, ShmEventLoopTest,
317 ::testing::Values(ReadMethod::COPY));
318INSTANTIATE_TEST_CASE_P(ShmEventLoopPinTest, ShmEventLoopTest,
319 ::testing::Values(ReadMethod::PIN));
320INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyDeathTest, ShmEventLoopDeathTest,
321 ::testing::Values(ReadMethod::COPY));
322INSTANTIATE_TEST_CASE_P(ShmEventLoopPinDeathTest, ShmEventLoopDeathTest,
323 ::testing::Values(ReadMethod::PIN));
324
Parker Schuhe4a70d62017-12-27 20:10:20 -0800325} // namespace testing
326} // namespace aos