Merge changes If50dcc0e,I17d591a5,I9c1cabda,Ib73d395b,I7e923b0d, ...
* changes:
Only schedule OnRun callbacks right after construction in aos
Split function scheduler out
Fix transmit timestamp logic in SimulatedNetworkBridge
Unit test for mismatched configs for server/client
Convert unreachable simulated network bridge code to CHECKs.
aos: Fix `--force_timestamp_loading` for single-node logs
Log, replay, and solve with transmit timestamps
Add boot info to NoncausalTimestampFilter debugging
Logger: Pipe the monotonic_remote_transmit_time through event loop
Remove unused kLogMessageAndDeliveryTime
Fix starter_test under msan
memory_mapped_queue: Use system_page_size
Logger: Define contract to record transmit time for reliable messages
Add note to static fbs Vector reserve() method
Add a test reproducing a log reading failure
Fix move ResizeableObject move constructor
Logger: Print solve number along with candidate solution for debug info
Call set_name() on more AOS timers
Fix crash in message_bridge_server
Fix typo in message_bridge_auth_client_lib.cc
Fix Python wheel generation
Fix LogReader destruction problem
Make doctests only compatible with x86_64
Fix possible data race in `aos/network/multinode_timestamp_filter.h`
Fix handling of empty C++ comments in JSON parsing
rename variables in aos Logger
Fix AOS logging when using non-EventLoop configuration
Add SendJson to AOS Sender
Add note on size units in file_operations.h
Reduce discrepancies between FlatbufferToJson versions
Reduce flakeness of shm_event_loop_test
aos: Make SetCurrentThreadAffinity errors a bit nicer
diff --git a/aos/configuration.cc b/aos/configuration.cc
index 6cae0d4..4de5332 100644
--- a/aos/configuration.cc
+++ b/aos/configuration.cc
@@ -1729,5 +1729,39 @@
return MergeWithConfig(config, new_channel_config);
}
+FlatbufferDetachedBuffer<Configuration> GetPartialConfiguration(
+ const Configuration &configuration,
+ std::function<bool(const Channel &)> should_include_channel) {
+ // create new_configuration1, containing everything except the `channels`
+ // field.
+ FlatbufferDetachedBuffer<Configuration> new_configuration1 =
+ RecursiveCopyFlatBuffer(&configuration);
+ new_configuration1.mutable_message()->clear_channels();
+
+ // create new_configuration2, containing only the `channels` field.
+ flatbuffers::FlatBufferBuilder fbb;
+ std::vector<flatbuffers::Offset<Channel>> new_channels_vec;
+ for (const auto &channel : *configuration.channels()) {
+ CHECK_NOTNULL(channel);
+ if (should_include_channel(*channel)) {
+ new_channels_vec.push_back(RecursiveCopyFlatBuffer(channel, &fbb));
+ }
+ }
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
+ new_channels_offset = fbb.CreateVector(new_channels_vec);
+ Configuration::Builder new_configuration2_builder(fbb);
+ new_configuration2_builder.add_channels(new_channels_offset);
+ fbb.Finish(new_configuration2_builder.Finish());
+ FlatbufferDetachedBuffer<Configuration> new_configuration2 = fbb.Release();
+
+ // Merge the configuration containing channels with the configuration
+ // containing everything else, creating a complete configuration.
+ const aos::FlatbufferDetachedBuffer<Configuration> raw_subset_configuration =
+ MergeFlatBuffers(&new_configuration1.message(),
+ &new_configuration2.message());
+
+ // Use MergeConfiguration to clean up redundant schemas.
+ return configuration::MergeConfiguration(raw_subset_configuration);
+}
} // namespace configuration
} // namespace aos
diff --git a/aos/configuration.h b/aos/configuration.h
index 01b9e7c..7ef2e6e 100644
--- a/aos/configuration.h
+++ b/aos/configuration.h
@@ -248,6 +248,12 @@
aos::FlatbufferVector<reflection::Schema> schema,
const aos::Node *source_node = nullptr, ChannelT overrides = {});
+// Build a new configuration that only contains the channels we want to
+// include. This is useful for excluding obsolete or deprecated channels, so
+// they don't appear in the configuration when reading the log.
+FlatbufferDetachedBuffer<Configuration> GetPartialConfiguration(
+ const Configuration &configuration,
+ std::function<bool(const Channel &)> should_include_channel);
} // namespace configuration
// Compare and equality operators for Channel. Note: these only check the name
diff --git a/aos/configuration_test.cc b/aos/configuration_test.cc
index e5fe303..eb08d78 100644
--- a/aos/configuration_test.cc
+++ b/aos/configuration_test.cc
@@ -1166,4 +1166,68 @@
ASSERT_EQ(971, channel->frequency());
}
+// Create a new configuration with the specified channel removed.
+// Initially there must be exactly one channel in the base_config that matches
+// the criteria. Check to make sure the new configuration has one less channel,
+// and that channel is the specified channel.
+void TestGetPartialConfiguration(const Configuration &base_config,
+ std::string_view test_channel_name,
+ std::string_view test_channel_type) {
+ const Channel *channel_from_base_config = GetChannel(
+ &base_config, test_channel_name, test_channel_type, "", nullptr);
+ ASSERT_TRUE(channel_from_base_config != nullptr);
+
+ const FlatbufferDetachedBuffer<Configuration> new_config =
+ configuration::GetPartialConfiguration(
+ base_config,
+ // should_include_channel function
+ [test_channel_name, test_channel_type](const Channel &channel) {
+ if (channel.name()->string_view() == test_channel_name &&
+ channel.type()->string_view() == test_channel_type) {
+ LOG(INFO) << "Omitting channel from save_log, channel: "
+ << channel.name()->string_view() << ", "
+ << channel.type()->string_view();
+ return false;
+ }
+ return true;
+ });
+
+ EXPECT_EQ(new_config.message().channels()->size(),
+ base_config.channels()->size() - 1);
+
+ channel_from_base_config = GetChannel(&base_config, test_channel_name,
+ test_channel_type, "", nullptr);
+ EXPECT_TRUE(channel_from_base_config != nullptr);
+
+ const Channel *channel_from_new_config =
+ GetChannel(new_config, test_channel_name, test_channel_type, "", nullptr);
+ EXPECT_TRUE(channel_from_new_config == nullptr);
+}
+
+// Tests that we can use a utility to remove individual channels from a
+// single-node config.
+TEST_F(ConfigurationTest, RemoveChannelsFromConfigSingleNode) {
+ FlatbufferDetachedBuffer<Configuration> base_config =
+ ReadConfig(ArtifactPath("aos/testdata/config1.json"));
+
+ constexpr std::string_view test_channel_name = "/foo2";
+ constexpr std::string_view test_channel_type = ".aos.bar";
+
+ TestGetPartialConfiguration(base_config.message(), test_channel_name,
+ test_channel_type);
+}
+
+// Tests that we can use a utility to remove individual channels from a
+// multi-node config.
+TEST_F(ConfigurationTest, RemoveChannelsFromConfigMultiNode) {
+ FlatbufferDetachedBuffer<Configuration> base_config =
+ ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
+
+ constexpr std::string_view test_channel_name = "/batman";
+ constexpr std::string_view test_channel_type = ".aos.baz";
+
+ TestGetPartialConfiguration(base_config.message(), test_channel_name,
+ test_channel_type);
+}
+
} // namespace aos::configuration::testing
diff --git a/aos/events/BUILD b/aos/events/BUILD
index e500417..7f9c008 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -509,10 +509,11 @@
":multinode_pingpong_test_combined_config",
":multinode_pingpong_test_split_config",
],
- shard_count = 4,
+ shard_count = 16,
target_compatible_with = ["@platforms//os:linux"],
deps = [
":event_loop_param_test",
+ ":function_scheduler",
":message_counter",
":ping_lib",
":pong_lib",
@@ -744,6 +745,17 @@
],
)
+cc_library(
+ name = "function_scheduler",
+ srcs = [
+ "function_scheduler.cc",
+ ],
+ hdrs = [
+ "function_scheduler.h",
+ ],
+ deps = [":event_loop"],
+)
+
cc_binary(
name = "aos_timing_report_streamer",
srcs = ["aos_timing_report_streamer.cc"],
diff --git a/aos/events/context.h b/aos/events/context.h
index 6648949..c50b5c0 100644
--- a/aos/events/context.h
+++ b/aos/events/context.h
@@ -24,6 +24,9 @@
monotonic_clock::time_point monotonic_remote_time;
realtime_clock::time_point realtime_remote_time;
+ // Time that the message was published over the network on the remote node.
+ monotonic_clock::time_point monotonic_remote_transmit_time;
+
// Index in the queue.
uint32_t queue_index;
// Index into the remote queue. Useful to determine if data was lost. In a
diff --git a/aos/events/event_loop.cc b/aos/events/event_loop.cc
index ba71b97..424971d 100644
--- a/aos/events/event_loop.cc
+++ b/aos/events/event_loop.cc
@@ -83,9 +83,11 @@
RawSender::Error RawSender::DoSend(
const SharedSpan data, monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid) {
return DoSend(data->data(), data->size(), monotonic_remote_time,
- realtime_remote_time, remote_queue_index, source_boot_uuid);
+ realtime_remote_time, monotonic_remote_transmit_time,
+ remote_queue_index, source_boot_uuid);
}
void RawSender::RecordSendResult(const Error error, size_t message_size) {
@@ -113,6 +115,7 @@
timing_(event_loop_->ChannelIndex(channel)) {
context_.monotonic_event_time = monotonic_clock::min_time;
context_.monotonic_remote_time = monotonic_clock::min_time;
+ context_.monotonic_remote_transmit_time = monotonic_clock::min_time;
context_.realtime_event_time = realtime_clock::min_time;
context_.realtime_remote_time = realtime_clock::min_time;
context_.queue_index = 0xffffffff;
@@ -628,6 +631,7 @@
void EventLoop::ClearContext() {
context_.monotonic_event_time = monotonic_clock::min_time;
context_.monotonic_remote_time = monotonic_clock::min_time;
+ context_.monotonic_remote_transmit_time = monotonic_clock::min_time;
context_.realtime_event_time = realtime_clock::min_time;
context_.realtime_remote_time = realtime_clock::min_time;
context_.queue_index = 0xffffffffu;
@@ -642,6 +646,7 @@
monotonic_clock::time_point monotonic_event_time) {
context_.monotonic_event_time = monotonic_event_time;
context_.monotonic_remote_time = monotonic_clock::min_time;
+ context_.monotonic_remote_transmit_time = monotonic_clock::min_time;
context_.realtime_event_time = realtime_clock::min_time;
context_.realtime_remote_time = realtime_clock::min_time;
context_.queue_index = 0xffffffffu;
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index 1ff0fb7..83393c2 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -140,12 +140,14 @@
// size() bytes into the data backed by data(). They then call Send to send.
// Returns Error::kOk on a successful send, or
// Error::kMessagesSentTooFast if messages were sent too fast. If provided,
- // monotonic_remote_time, realtime_remote_time, and remote_queue_index are
- // attached to the message and are available in the context on the read side.
- // If they are not populated, the read side will get the sent times instead.
+ // monotonic_remote_time, realtime_remote_time,
+ // monotonic_remote_transmit_time, and remote_queue_index are attached to the
+ // message and are available in the context on the read side. If they are not
+ // populated, the read side will get the sent times instead.
Error Send(size_t size);
Error Send(size_t size, monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid);
// Sends a single block of data by copying it.
@@ -155,6 +157,7 @@
Error Send(const void *data, size_t size,
monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid);
// CHECKs that no sending Error occurred and logs the channel_ data if
@@ -168,6 +171,7 @@
Error Send(const SharedSpan data,
monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &remote_boot_uuid);
const Channel *channel() const { return channel_; }
@@ -216,21 +220,22 @@
private:
friend class EventLoop;
- virtual Error DoSend(const void *data, size_t size,
- monotonic_clock::time_point monotonic_remote_time,
- realtime_clock::time_point realtime_remote_time,
- uint32_t remote_queue_index,
- const UUID &source_boot_uuid) = 0;
- virtual Error DoSend(size_t size,
- monotonic_clock::time_point monotonic_remote_time,
- realtime_clock::time_point realtime_remote_time,
- uint32_t remote_queue_index,
- const UUID &source_boot_uuid) = 0;
- virtual Error DoSend(const SharedSpan data,
- monotonic_clock::time_point monotonic_remote_time,
- realtime_clock::time_point realtime_remote_time,
- uint32_t remote_queue_index,
- const UUID &source_boot_uuid);
+ virtual Error DoSend(
+ const void *data, size_t size,
+ monotonic_clock::time_point monotonic_remote_time,
+ realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
+ uint32_t remote_queue_index, const UUID &source_boot_uuid) = 0;
+ virtual Error DoSend(
+ size_t size, monotonic_clock::time_point monotonic_remote_time,
+ realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
+ uint32_t remote_queue_index, const UUID &source_boot_uuid) = 0;
+ virtual Error DoSend(
+ const SharedSpan data, monotonic_clock::time_point monotonic_remote_time,
+ realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
+ uint32_t remote_queue_index, const UUID &source_boot_uuid);
void RecordSendResult(const Error error, size_t message_size);
@@ -506,6 +511,16 @@
// the buffer the caller can fill out.
int buffer_index() const { return CHECK_NOTNULL(sender_)->buffer_index(); }
+ // Convenience function to build and send a message created from JSON
+ // representation.
+ RawSender::Error SendJson(std::string_view json) {
+ auto builder = MakeBuilder();
+ flatbuffers::Offset<T> json_offset =
+ aos::JsonToFlatbuffer<T>(json, builder.fbb());
+ CHECK(!json_offset.IsNull()) << ": Invalid JSON";
+ return builder.Send(json_offset);
+ }
+
private:
friend class EventLoop;
Sender(std::unique_ptr<RawSender> sender) : sender_(std::move(sender)) {}
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
index 58bfd9b..4885f5c 100644
--- a/aos/events/event_loop_param_test.cc
+++ b/aos/events/event_loop_param_test.cc
@@ -133,6 +133,33 @@
EXPECT_TRUE(happened);
}
+// Tests that a static sender's Builder object can be moved safely.
+TEST_P(AbstractEventLoopTest, StaticBuilderMoveConstructor) {
+ auto loop1 = MakePrimary();
+
+ aos::Sender<TestMessageStatic> sender =
+ loop1->MakeSender<TestMessageStatic>("/test");
+ aos::Fetcher<TestMessage> fetcher = loop1->MakeFetcher<TestMessage>("/test");
+ std::optional<aos::Sender<TestMessageStatic>::StaticBuilder> moved_to_builder;
+ {
+ aos::Sender<TestMessageStatic>::StaticBuilder moved_from_builder =
+ sender.MakeStaticBuilder();
+ moved_to_builder.emplace(std::move(moved_from_builder));
+ }
+
+ loop1->OnRun([this, &moved_to_builder]() {
+ moved_to_builder.value()->set_value(200);
+ CHECK(moved_to_builder.value().builder()->Verify());
+ moved_to_builder.value().CheckOk(moved_to_builder.value().Send());
+ this->Exit();
+ });
+
+ ASSERT_FALSE(fetcher.Fetch());
+ Run();
+ ASSERT_TRUE(fetcher.Fetch());
+ EXPECT_EQ(200, fetcher->value());
+}
+
// Tests that watcher can receive messages from a sender, sent via SendDetached.
TEST_P(AbstractEventLoopTest, BasicSendDetached) {
auto loop1 = Make();
@@ -162,6 +189,29 @@
EXPECT_EQ(fetcher->value(), 200);
}
+// Tests that fetcher can receive messages from a sender, sent via SendJson.
+TEST_P(AbstractEventLoopTest, BasicSendJson) {
+ auto loop1 = Make();
+ auto loop2 = MakePrimary();
+
+ aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
+ sender.CheckOk(sender.SendJson(R"json({"value":201})json"));
+
+ auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+ ASSERT_TRUE(fetcher.Fetch());
+ EXPECT_EQ(fetcher->value(), 201);
+}
+
+// Tests that invalid JSON isn't sent.
+TEST_P(AbstractEventLoopDeathTest, InvalidSendJson) {
+ auto loop1 = Make();
+ auto loop2 = MakePrimary();
+
+ aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
+ EXPECT_DEATH({ sender.CheckOk(sender.SendJson(R"json({"val)json")); },
+ "Invalid JSON");
+}
+
// Verifies that a no-arg watcher will not have a data pointer.
TEST_P(AbstractEventLoopTest, NoArgNoData) {
auto loop1 = Make();
@@ -308,6 +358,8 @@
EXPECT_EQ(fetcher.context().monotonic_event_time, monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().monotonic_remote_time, monotonic_clock::min_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().realtime_event_time, realtime_clock::min_time);
EXPECT_EQ(fetcher.context().realtime_remote_time, realtime_clock::min_time);
EXPECT_EQ(fetcher.context().source_boot_uuid, UUID::Zero());
@@ -331,6 +383,8 @@
const aos::realtime_clock::time_point realtime_now = loop2->realtime_now();
EXPECT_EQ(fetcher.context().monotonic_event_time,
fetcher.context().monotonic_remote_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().realtime_event_time,
fetcher.context().realtime_remote_time);
@@ -379,6 +433,8 @@
monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().monotonic_remote_time,
monotonic_clock::min_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().realtime_event_time, realtime_clock::min_time);
EXPECT_EQ(fetcher.context().realtime_remote_time, realtime_clock::min_time);
EXPECT_EQ(fetcher.context().source_boot_uuid, UUID::Zero());
@@ -399,6 +455,8 @@
EXPECT_EQ(fetcher.context().monotonic_event_time, monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().monotonic_remote_time, monotonic_clock::min_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().realtime_event_time, realtime_clock::min_time);
EXPECT_EQ(fetcher.context().realtime_remote_time, realtime_clock::min_time);
EXPECT_EQ(fetcher.context().source_boot_uuid, UUID::Zero());
@@ -420,6 +478,8 @@
fetcher.context().monotonic_remote_time);
EXPECT_EQ(fetcher.context().realtime_event_time,
fetcher.context().realtime_remote_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_GE(fetcher.context().monotonic_event_time, monotonic_now - kEpsilon);
EXPECT_LE(fetcher.context().monotonic_event_time, monotonic_now + kEpsilon);
@@ -1414,6 +1474,8 @@
auto test_timer = loop->AddTimer([this, ×, &expected_times, &loop]() {
times.push_back(loop->monotonic_now());
EXPECT_EQ(loop->context().monotonic_remote_time, monotonic_clock::min_time);
+ EXPECT_EQ(loop->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(loop->context().realtime_event_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().realtime_remote_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().source_boot_uuid, loop->boot_uuid());
@@ -1864,6 +1926,8 @@
EXPECT_EQ(loop1->context().realtime_remote_time,
loop1->context().realtime_event_time);
EXPECT_EQ(loop1->context().source_boot_uuid, loop1->boot_uuid());
+ EXPECT_EQ(loop1->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
const aos::monotonic_clock::time_point monotonic_now =
loop1->monotonic_now();
@@ -1911,6 +1975,8 @@
fetcher.context().realtime_remote_time);
EXPECT_EQ(fetcher.context().monotonic_event_time,
fetcher.context().monotonic_remote_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().source_boot_uuid, loop1->boot_uuid());
EXPECT_TRUE(monotonic_time_offset > ::std::chrono::milliseconds(-500))
@@ -1962,6 +2028,8 @@
EXPECT_EQ(loop1->context().monotonic_remote_time,
loop1->context().monotonic_event_time);
+ EXPECT_EQ(loop1->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(loop1->context().realtime_remote_time,
loop1->context().realtime_event_time);
EXPECT_EQ(loop1->context().source_boot_uuid, loop1->boot_uuid());
@@ -1998,6 +2066,8 @@
fetcher.context().realtime_remote_time);
EXPECT_EQ(fetcher.context().monotonic_event_time,
fetcher.context().monotonic_remote_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(fetcher.context().source_boot_uuid, loop1->boot_uuid());
EXPECT_TRUE(monotonic_time_offset > ::std::chrono::milliseconds(-500))
@@ -2056,6 +2126,8 @@
EXPECT_EQ(loop1->context().monotonic_remote_time,
monotonic_clock::min_time);
+ EXPECT_EQ(loop1->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(loop1->context().source_boot_uuid, loop1->boot_uuid());
EXPECT_EQ(loop1->context().realtime_event_time,
realtime_clock::min_time);
@@ -3066,6 +3138,8 @@
aos::monotonic_clock::time_point(chrono::seconds(1501));
const aos::realtime_clock::time_point realtime_remote_time =
aos::realtime_clock::time_point(chrono::seconds(3132));
+ const aos::monotonic_clock::time_point monotonic_remote_transmit_time =
+ aos::monotonic_clock::time_point(chrono::seconds(1601));
const uint32_t remote_queue_index = 0x254971;
const UUID source_boot_uuid = UUID::Random();
@@ -3080,7 +3154,8 @@
loop2->OnRun([&]() {
EXPECT_EQ(sender->Send(kMessage.span().data(), kMessage.span().size(),
monotonic_remote_time, realtime_remote_time,
- remote_queue_index, source_boot_uuid),
+ monotonic_remote_transmit_time, remote_queue_index,
+ source_boot_uuid),
RawSender::Error::kOk);
});
@@ -3088,9 +3163,9 @@
loop2->MakeRawWatcher(
configuration::GetChannel(loop2->configuration(), "/test",
"aos.TestMessage", "", nullptr),
- [this, monotonic_remote_time, realtime_remote_time, source_boot_uuid,
- remote_queue_index, &fetcher,
- &happened](const Context &context, const void * /*message*/) {
+ [this, monotonic_remote_time, realtime_remote_time,
+ monotonic_remote_transmit_time, source_boot_uuid, remote_queue_index,
+ &fetcher, &happened](const Context &context, const void * /*message*/) {
happened = true;
EXPECT_EQ(monotonic_remote_time, context.monotonic_remote_time);
EXPECT_EQ(realtime_remote_time, context.realtime_remote_time);
@@ -3102,6 +3177,8 @@
fetcher->context().monotonic_remote_time);
EXPECT_EQ(realtime_remote_time,
fetcher->context().realtime_remote_time);
+ EXPECT_EQ(monotonic_remote_transmit_time,
+ fetcher->context().monotonic_remote_transmit_time);
this->Exit();
});
@@ -3109,6 +3186,19 @@
EXPECT_FALSE(happened);
Run();
EXPECT_TRUE(happened);
+
+ // Confirm everything goes back.
+ EXPECT_EQ(loop2->context().monotonic_event_time, monotonic_clock::min_time);
+ EXPECT_EQ(loop2->context().monotonic_remote_time, monotonic_clock::min_time);
+ EXPECT_EQ(loop2->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
+ EXPECT_EQ(loop2->context().realtime_event_time, realtime_clock::min_time);
+ EXPECT_EQ(loop2->context().realtime_remote_time, realtime_clock::min_time);
+ EXPECT_EQ(loop2->context().source_boot_uuid, loop2->boot_uuid());
+ EXPECT_EQ(loop2->context().queue_index, 0xffffffffu);
+ EXPECT_EQ(loop2->context().size, 0u);
+ EXPECT_EQ(loop2->context().data, nullptr);
+ EXPECT_EQ(loop2->context().buffer_index, -1);
}
// Tests that a raw sender fills out sent data.
@@ -3356,6 +3446,8 @@
EXPECT_EQ(loop->context().monotonic_event_time, monotonic_clock::min_time);
EXPECT_EQ(loop->context().monotonic_remote_time, monotonic_clock::min_time);
+ EXPECT_EQ(loop->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(loop->context().realtime_event_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().realtime_remote_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().source_boot_uuid, loop->boot_uuid());
@@ -3373,6 +3465,8 @@
monotonic_event_time_on_run = loop->context().monotonic_event_time;
EXPECT_LE(monotonic_event_time_on_run, loop->monotonic_now());
EXPECT_EQ(loop->context().monotonic_remote_time, monotonic_clock::min_time);
+ EXPECT_EQ(loop->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(loop->context().realtime_event_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().realtime_remote_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().source_boot_uuid, loop->boot_uuid());
@@ -3392,6 +3486,8 @@
EXPECT_EQ(loop->context().monotonic_event_time, monotonic_clock::min_time);
EXPECT_EQ(loop->context().monotonic_remote_time, monotonic_clock::min_time);
+ EXPECT_EQ(loop->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
EXPECT_EQ(loop->context().realtime_event_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().realtime_remote_time, realtime_clock::min_time);
EXPECT_EQ(loop->context().source_boot_uuid, loop->boot_uuid());
@@ -3553,6 +3649,7 @@
// RawSender::Error::kMessagesSentTooFast.
TEST_P(AbstractEventLoopTest, SendingMessagesTooFast) {
auto event_loop = MakePrimary();
+ event_loop->SetRuntimeRealtimePriority(5);
auto sender = event_loop->MakeSender<TestMessage>("/test");
@@ -3567,21 +3664,29 @@
int msgs_sent = 1;
const int queue_size = TestChannelQueueSize(event_loop.get());
+ const int messages_per_ms = 2;
+ const auto kRepeatOffset = std::chrono::milliseconds(10);
+ const auto base_offset =
+ configuration::ChannelStorageDuration(event_loop->configuration(),
+ sender.channel()) -
+ (std::chrono::milliseconds(1) * (queue_size / 2) / messages_per_ms);
+
const auto timer = event_loop->AddTimer([&]() {
- const bool done = (msgs_sent == queue_size + 1);
- ASSERT_EQ(
- SendTestMessage(sender),
- done ? RawSender::Error::kMessagesSentTooFast : RawSender::Error::kOk);
- msgs_sent++;
- if (done) {
- Exit();
+ // Send in bursts to reduce scheduler load to make the test more
+ // reproducible.
+ for (int i = 0; i < messages_per_ms * kRepeatOffset.count(); ++i) {
+ const bool done = (msgs_sent == queue_size + 1);
+ ASSERT_EQ(SendTestMessage(sender),
+ done ? RawSender::Error::kMessagesSentTooFast
+ : RawSender::Error::kOk);
+ msgs_sent++;
+ if (done) {
+ Exit();
+ return;
+ }
}
});
- const auto kRepeatOffset = std::chrono::milliseconds(1);
- const auto base_offset = configuration::ChannelStorageDuration(
- event_loop->configuration(), sender.channel()) -
- (kRepeatOffset * (queue_size / 2));
event_loop->OnRun([&event_loop, &timer, &base_offset, &kRepeatOffset]() {
timer->Schedule(event_loop->monotonic_now() + base_offset, kRepeatOffset);
});
@@ -3595,6 +3700,7 @@
// situation
TEST_P(AbstractEventLoopTest, SendingAfterSendingTooFast) {
auto event_loop = MakePrimary();
+ event_loop->SetRuntimeRealtimePriority(5);
auto sender = event_loop->MakeSender<TestMessage>("/test");
diff --git a/aos/events/event_loop_runtime.h b/aos/events/event_loop_runtime.h
index 0505852..afc96e2 100644
--- a/aos/events/event_loop_runtime.h
+++ b/aos/events/event_loop_runtime.h
@@ -25,6 +25,7 @@
int64_t monotonic_remote_time;
int64_t realtime_remote_time;
+ int64_t monotonic_remote_transmit_time;
uint32_t queue_index;
uint32_t remote_queue_index;
@@ -48,6 +49,8 @@
offsetof(RustContext, monotonic_remote_time));
static_assert(offsetof(Context, realtime_remote_time) ==
offsetof(RustContext, realtime_remote_time));
+static_assert(offsetof(Context, monotonic_remote_transmit_time) ==
+ offsetof(RustContext, monotonic_remote_transmit_time));
static_assert(offsetof(Context, queue_index) ==
offsetof(RustContext, queue_index));
static_assert(offsetof(Context, remote_queue_index) ==
diff --git a/aos/events/event_loop_tmpl.h b/aos/events/event_loop_tmpl.h
index cc8e5c3..3f41ec1 100644
--- a/aos/events/event_loop_tmpl.h
+++ b/aos/events/event_loop_tmpl.h
@@ -197,15 +197,17 @@
inline RawSender::Error RawSender::Send(size_t size) {
return Send(size, monotonic_clock::min_time, realtime_clock::min_time,
- 0xffffffffu, event_loop_->boot_uuid());
+ monotonic_clock::min_time, 0xffffffffu, event_loop_->boot_uuid());
}
inline RawSender::Error RawSender::Send(
size_t size, aos::monotonic_clock::time_point monotonic_remote_time,
aos::realtime_clock::time_point realtime_remote_time,
+ aos::monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &uuid) {
- const auto err = DoSend(size, monotonic_remote_time, realtime_remote_time,
- remote_queue_index, uuid);
+ const auto err =
+ DoSend(size, monotonic_remote_time, realtime_remote_time,
+ monotonic_remote_transmit_time, remote_queue_index, uuid);
RecordSendResult(err, size);
if (err == Error::kOk) {
ftrace_.FormatMessage(
@@ -219,16 +221,18 @@
inline RawSender::Error RawSender::Send(const void *data, size_t size) {
return Send(data, size, monotonic_clock::min_time, realtime_clock::min_time,
- 0xffffffffu, event_loop_->boot_uuid());
+ monotonic_clock::min_time, 0xffffffffu, event_loop_->boot_uuid());
}
inline RawSender::Error RawSender::Send(
const void *data, size_t size,
aos::monotonic_clock::time_point monotonic_remote_time,
aos::realtime_clock::time_point realtime_remote_time,
+ aos::monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &uuid) {
- const auto err = DoSend(data, size, monotonic_remote_time,
- realtime_remote_time, remote_queue_index, uuid);
+ const auto err =
+ DoSend(data, size, monotonic_remote_time, realtime_remote_time,
+ monotonic_remote_transmit_time, remote_queue_index, uuid);
RecordSendResult(err, size);
if (err == RawSender::Error::kOk) {
ftrace_.FormatMessage(
@@ -242,17 +246,20 @@
inline RawSender::Error RawSender::Send(const SharedSpan data) {
return Send(std::move(data), monotonic_clock::min_time,
- realtime_clock::min_time, 0xffffffffu, event_loop_->boot_uuid());
+ realtime_clock::min_time, monotonic_clock::min_time, 0xffffffffu,
+ event_loop_->boot_uuid());
}
inline RawSender::Error RawSender::Send(
const SharedSpan data,
aos::monotonic_clock::time_point monotonic_remote_time,
aos::realtime_clock::time_point realtime_remote_time,
+ aos::monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &uuid) {
const size_t size = data->size();
- const auto err = DoSend(std::move(data), monotonic_remote_time,
- realtime_remote_time, remote_queue_index, uuid);
+ const auto err =
+ DoSend(std::move(data), monotonic_remote_time, realtime_remote_time,
+ monotonic_remote_transmit_time, remote_queue_index, uuid);
RecordSendResult(err, size);
if (err == Error::kOk) {
ftrace_.FormatMessage(
diff --git a/aos/events/event_scheduler.cc b/aos/events/event_scheduler.cc
index ae50917..df6ab81 100644
--- a/aos/events/event_scheduler.cc
+++ b/aos/events/event_scheduler.cc
@@ -68,6 +68,7 @@
void EventScheduler::Startup() {
++boot_count_;
+ cached_event_list_monotonic_time_ = kInvalidCachedTime();
CHECK(!is_running_);
MaybeRunOnStartup();
CHECK(called_started_);
diff --git a/aos/events/event_scheduler.h b/aos/events/event_scheduler.h
index 55fa9a4..84201ed 100644
--- a/aos/events/event_scheduler.h
+++ b/aos/events/event_scheduler.h
@@ -210,6 +210,10 @@
void MaybeRunOnStartup();
void MaybeRunOnRun();
+ constexpr monotonic_clock::time_point kInvalidCachedTime() {
+ return monotonic_clock::max_time;
+ }
+
// Current execution time.
monotonic_clock::time_point monotonic_now_ = monotonic_clock::epoch();
@@ -237,7 +241,7 @@
bool called_started_ = false;
std::optional<distributed_clock::time_point> cached_epoch_;
monotonic_clock::time_point cached_event_list_monotonic_time_ =
- monotonic_clock::max_time;
+ kInvalidCachedTime();
distributed_clock::time_point cached_event_list_time_ =
distributed_clock::max_time;
diff --git a/aos/events/function_scheduler.cc b/aos/events/function_scheduler.cc
new file mode 100644
index 0000000..94cb90d
--- /dev/null
+++ b/aos/events/function_scheduler.cc
@@ -0,0 +1,34 @@
+#include "aos/events/function_scheduler.h"
+
+namespace aos {
+
+FunctionScheduler::FunctionScheduler(aos::EventLoop *event_loop)
+ : event_loop_(event_loop), timer_(event_loop_->AddTimer([this]() {
+ RunFunctions(event_loop_->context().monotonic_event_time);
+ })) {
+ timer_->set_name("function_timer");
+ event_loop_->OnRun(
+ [this]() { RunFunctions(event_loop_->context().monotonic_event_time); });
+}
+
+void FunctionScheduler::ScheduleAt(std::function<void()> &&function,
+ aos::monotonic_clock::time_point time) {
+ functions_.insert(std::make_pair(time, std::move(function)));
+ timer_->Schedule(functions_.begin()->first);
+}
+
+void FunctionScheduler::RunFunctions(aos::monotonic_clock::time_point now) {
+ while (true) {
+ if (functions_.empty()) return;
+ if (functions_.begin()->first > now) {
+ break;
+ }
+ CHECK_EQ(functions_.begin()->first, now);
+
+ functions_.begin()->second();
+ functions_.erase(functions_.begin());
+ }
+ timer_->Schedule(functions_.begin()->first);
+}
+
+} // namespace aos
diff --git a/aos/events/function_scheduler.h b/aos/events/function_scheduler.h
new file mode 100644
index 0000000..7a846c6
--- /dev/null
+++ b/aos/events/function_scheduler.h
@@ -0,0 +1,33 @@
+#ifndef AOS_EVENTS_FUNCTION_SCHEDULER_H_
+#define AOS_EVENTS_FUNCTION_SCHEDULER_H_
+
+#include <functional>
+#include <map>
+
+#include "aos/events/event_loop.h"
+#include "aos/time/time.h"
+
+namespace aos {
+
+// Simple class to call a function at a time with a timer.
+class FunctionScheduler {
+ public:
+ FunctionScheduler(aos::EventLoop *event_loop);
+
+ // Schedules the function to be run at the provided time.
+ void ScheduleAt(std::function<void()> &&function,
+ aos::monotonic_clock::time_point time);
+
+ private:
+ void RunFunctions(aos::monotonic_clock::time_point now);
+
+ aos::EventLoop *event_loop_;
+ aos::TimerHandler *timer_;
+
+ std::multimap<aos::monotonic_clock::time_point, std::function<void()>>
+ functions_;
+};
+
+} // namespace aos
+
+#endif // AOS_EVENTS_FUNCTION_SCHEDULER_H_
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index a8979ee..eadefa8 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -807,6 +807,20 @@
)
aos_config(
+ name = "multinode_pingpong_reboot_reliable_only_config",
+ src = "multinode_pingpong_reboot_reliable_only.json",
+ flatbuffers = [
+ "//aos/events:ping_fbs",
+ "//aos/network:message_bridge_client_fbs",
+ "//aos/network:message_bridge_server_fbs",
+ "//aos/network:remote_message_fbs",
+ "//aos/network:timestamp_fbs",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = ["//aos/events:aos_config"],
+)
+
+aos_config(
name = "multinode_pingpong_reboot_ooo_config",
src = "multinode_pingpong_reboot_ooo.json",
flatbuffers = [
@@ -895,6 +909,7 @@
":multinode_pingpong_combined_config",
":multinode_pingpong_pi3_pingpong_config",
":multinode_pingpong_reboot_ooo_config",
+ ":multinode_pingpong_reboot_reliable_only_config",
":multinode_pingpong_split3_config",
":multinode_pingpong_split4_config",
":multinode_pingpong_split4_mixed1_config",
diff --git a/aos/events/logging/file_operations.h b/aos/events/logging/file_operations.h
index 538ae60..0c2133d 100644
--- a/aos/events/logging/file_operations.h
+++ b/aos/events/logging/file_operations.h
@@ -16,7 +16,7 @@
public:
struct File {
std::string name;
- size_t size;
+ size_t size; // bytes.
};
virtual ~FileOperations() = default;
diff --git a/aos/events/logging/log_namer.cc b/aos/events/logging/log_namer.cc
index 3bbca3b..3d52070 100644
--- a/aos/events/logging/log_namer.cc
+++ b/aos/events/logging/log_namer.cc
@@ -91,6 +91,10 @@
monotonic_clock::max_time;
state.oldest_logger_local_unreliable_monotonic_timestamp =
monotonic_clock::max_time;
+ state.oldest_remote_reliable_monotonic_transmit_timestamp =
+ monotonic_clock::max_time;
+ state.oldest_local_reliable_monotonic_transmit_timestamp =
+ monotonic_clock::max_time;
}
state_[node_index_].boot_uuid = source_node_boot_uuid;
@@ -115,6 +119,7 @@
void NewDataWriter::UpdateRemote(
const size_t remote_node_index, const UUID &remote_node_boot_uuid,
const monotonic_clock::time_point monotonic_remote_time,
+ const monotonic_clock::time_point monotonic_remote_transmit_time,
const monotonic_clock::time_point monotonic_event_time, const bool reliable,
monotonic_clock::time_point monotonic_timestamp_time) {
// Trigger rotation if anything in the header changes.
@@ -140,9 +145,29 @@
monotonic_clock::max_time;
state.oldest_logger_local_unreliable_monotonic_timestamp =
monotonic_clock::max_time;
+ state.oldest_remote_reliable_monotonic_transmit_timestamp =
+ monotonic_clock::max_time;
+ state.oldest_local_reliable_monotonic_transmit_timestamp =
+ monotonic_clock::max_time;
rotate = true;
}
+ if (monotonic_remote_transmit_time != monotonic_clock::min_time) {
+ if (state.oldest_remote_reliable_monotonic_transmit_timestamp >
+ monotonic_remote_transmit_time) {
+ VLOG(1) << name() << " Remote " << remote_node_index
+ << " oldest_remote_reliable_monotonic_transmit_timestamp updated "
+ "from "
+ << state.oldest_remote_reliable_monotonic_transmit_timestamp
+ << " to " << monotonic_remote_transmit_time;
+ state.oldest_remote_reliable_monotonic_transmit_timestamp =
+ monotonic_remote_transmit_time;
+ state.oldest_local_reliable_monotonic_transmit_timestamp =
+ monotonic_event_time;
+ rotate = true;
+ }
+ }
+
// Did the unreliable timestamps change?
if (!reliable) {
if (state.oldest_remote_unreliable_monotonic_timestamp >
@@ -395,7 +420,10 @@
const UUID &source_node_boot_uuid = state[node_index].boot_uuid;
const Node *const source_node =
configuration::GetNode(configuration_, node_index);
- CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 35u);
+ CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 37u)
+ << ": If you added new fields to the LogFileHeader table, don't forget "
+ "to add it below!";
+ ;
flatbuffers::FlatBufferBuilder fbb;
fbb.ForceDefaults(true);
@@ -494,6 +522,14 @@
oldest_logger_local_unreliable_monotonic_timestamps_offset =
fbb.CreateUninitializedVector(state.size(), &unused);
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>>
+ oldest_remote_reliable_monotonic_transmit_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
+
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>>
+ oldest_local_reliable_monotonic_transmit_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
+
for (size_t i = 0; i < state.size(); ++i) {
if (state[i].boot_uuid != UUID::Zero()) {
boot_uuid_offsets.emplace_back(state[i].boot_uuid.PackString(&fbb));
@@ -517,6 +553,10 @@
monotonic_clock::max_time);
CHECK_EQ(state[i].oldest_logger_local_unreliable_monotonic_timestamp,
monotonic_clock::max_time);
+ CHECK_EQ(state[i].oldest_remote_reliable_monotonic_transmit_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(state[i].oldest_local_reliable_monotonic_transmit_timestamp,
+ monotonic_clock::max_time);
}
flatbuffers::GetMutableTemporaryPointer(
@@ -567,6 +607,19 @@
.oldest_logger_local_unreliable_monotonic_timestamp
.time_since_epoch()
.count());
+
+ flatbuffers::GetMutableTemporaryPointer(
+ fbb, oldest_remote_reliable_monotonic_transmit_timestamps_offset)
+ ->Mutate(i, state[i]
+ .oldest_remote_reliable_monotonic_transmit_timestamp
+ .time_since_epoch()
+ .count());
+ flatbuffers::GetMutableTemporaryPointer(
+ fbb, oldest_local_reliable_monotonic_transmit_timestamps_offset)
+ ->Mutate(i, state[i]
+ .oldest_local_reliable_monotonic_transmit_timestamp
+ .time_since_epoch()
+ .count());
}
flatbuffers::Offset<
@@ -678,6 +731,12 @@
log_file_header_builder
.add_oldest_logger_local_unreliable_monotonic_timestamps(
oldest_logger_local_unreliable_monotonic_timestamps_offset);
+ log_file_header_builder
+ .add_oldest_remote_reliable_monotonic_transmit_timestamps(
+ oldest_remote_reliable_monotonic_transmit_timestamps_offset);
+ log_file_header_builder
+ .add_oldest_local_reliable_monotonic_transmit_timestamps(
+ oldest_local_reliable_monotonic_transmit_timestamps_offset);
log_file_header_builder.add_data_stored(data_stored_offset);
fbb.FinishSizePrefixed(log_file_header_builder.Finish());
diff --git a/aos/events/logging/log_namer.h b/aos/events/logging/log_namer.h
index e0660eb..a0d5857 100644
--- a/aos/events/logging/log_namer.h
+++ b/aos/events/logging/log_namer.h
@@ -70,6 +70,7 @@
// message is from.
void UpdateRemote(size_t remote_node_index, const UUID &remote_node_boot_uuid,
monotonic_clock::time_point monotonic_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
monotonic_clock::time_point monotonic_event_time,
bool reliable,
monotonic_clock::time_point monotonic_timestamp_time =
@@ -147,6 +148,17 @@
monotonic_clock::time_point
oldest_logger_local_unreliable_monotonic_timestamp =
monotonic_clock::max_time;
+
+ // Transmit timestamp on the remote monotonic clock of the oldest message
+ // sent to node_index_.
+ monotonic_clock::time_point
+ oldest_remote_reliable_monotonic_transmit_timestamp =
+ monotonic_clock::max_time;
+ // Timestamp on the local monotonic clock of the message in
+ // oldest_remote_reliable_monotonic_transmit_timestamp.
+ monotonic_clock::time_point
+ oldest_local_reliable_monotonic_transmit_timestamp =
+ monotonic_clock::max_time;
};
private:
diff --git a/aos/events/logging/log_reader.cc b/aos/events/logging/log_reader.cc
index 5b437ba..df1ed8c 100644
--- a/aos/events/logging/log_reader.cc
+++ b/aos/events/logging/log_reader.cc
@@ -1177,7 +1177,9 @@
// Make sure that things get destroyed in the correct order, rather than
// relying on getting the order correct in the class definition.
for (std::unique_ptr<State> &state : states_) {
- state->Deregister();
+ if (state) {
+ state->Deregister();
+ }
}
event_loop_factory_unique_ptr_.reset();
@@ -1519,7 +1521,9 @@
const RawSender::Error err = sender->Send(
SharedSpan(timestamped_message.data, ×tamped_message.data->span),
timestamped_message.monotonic_remote_time.time,
- timestamped_message.realtime_remote_time, remote_queue_index,
+ timestamped_message.realtime_remote_time,
+ timestamped_message.monotonic_remote_transmit_time.time,
+ remote_queue_index,
(channel_source_state_[timestamped_message.channel_index] != nullptr
? CHECK_NOTNULL(multinode_filters_)
->boot_uuid(configuration::GetNodeIndex(
@@ -1612,6 +1616,10 @@
message_header_builder.add_monotonic_remote_time(
timestamped_message.monotonic_remote_time.time.time_since_epoch()
.count());
+ message_header_builder.add_monotonic_remote_transmit_time(
+ timestamped_message.monotonic_remote_transmit_time.time
+ .time_since_epoch()
+ .count());
message_header_builder.add_realtime_remote_time(
timestamped_message.realtime_remote_time.time_since_epoch().count());
diff --git a/aos/events/logging/log_writer.cc b/aos/events/logging/log_writer.cc
index f840c22..c960ab9 100644
--- a/aos/events/logging/log_writer.cc
+++ b/aos/events/logging/log_writer.cc
@@ -41,23 +41,7 @@
// from the channel index that the event loop uses to the channel index in
// the config in the log file.
event_loop_to_logged_channel_index_.resize(
- event_loop->configuration()->channels()->size(), -1);
- for (size_t event_loop_channel_index = 0;
- event_loop_channel_index <
- event_loop->configuration()->channels()->size();
- ++event_loop_channel_index) {
- const Channel *event_loop_channel =
- event_loop->configuration()->channels()->Get(event_loop_channel_index);
-
- const Channel *logged_channel = aos::configuration::GetChannel(
- configuration_, event_loop_channel->name()->string_view(),
- event_loop_channel->type()->string_view(), "", node_);
-
- if (logged_channel != nullptr) {
- event_loop_to_logged_channel_index_[event_loop_channel_index] =
- configuration::ChannelIndex(configuration_, logged_channel);
- }
- }
+ event_loop->configuration()->channels()->size());
// Map to match source channels with the timestamp logger, if the contents
// should be reliable, and a list of all channels logged on it to be treated
@@ -92,6 +76,18 @@
const Channel *const timestamp_logger_channel =
finder.ForChannel(channel, connection);
+ const Channel *logged_channel = aos::configuration::GetChannel(
+ configuration_, channel->name()->string_view(),
+ channel->type()->string_view(), "", node_);
+ if (logged_channel == nullptr) {
+ // The channel doesn't exist in configuration_, so don't log it.
+ continue;
+ }
+ if (!should_log(logged_channel)) {
+ // The channel didn't pass our `should_log` check, so don't log it.
+ continue;
+ }
+
auto it = timestamp_logger_channels.find(timestamp_logger_channel);
if (it != timestamp_logger_channels.end()) {
CHECK(!is_split);
@@ -120,47 +116,61 @@
}
}
- for (size_t channel_index = 0;
- channel_index < configuration_->channels()->size(); ++channel_index) {
- const Channel *const config_channel =
- configuration_->channels()->Get(channel_index);
+ for (size_t event_loop_channel_index = 0;
+ event_loop_channel_index <
+ event_loop->configuration()->channels()->size();
+ ++event_loop_channel_index) {
+ const Channel *event_loop_channel =
+ event_loop->configuration()->channels()->Get(event_loop_channel_index);
+
// The MakeRawFetcher method needs a channel which is in the event loop
// configuration() object, not the configuration_ object. Go look that up
// from the config.
- const Channel *channel = aos::configuration::GetChannel(
- event_loop_->configuration(), config_channel->name()->string_view(),
- config_channel->type()->string_view(), "", event_loop_->node());
- CHECK(channel != nullptr)
- << ": Failed to look up channel "
- << aos::configuration::CleanedChannelToString(config_channel);
- if (!should_log(config_channel)) {
+ const Channel *logged_channel = aos::configuration::GetChannel(
+ configuration_, event_loop_channel->name()->string_view(),
+ event_loop_channel->type()->string_view(), "", node_);
+
+ if (logged_channel == nullptr) {
+ // The channel doesn't exist in configuration_, so don't log the
+ // timestamps.
+ continue;
+ }
+ if (!should_log(logged_channel)) {
+ // The channel didn't pass our `should_log` check, so don't log the
+ // timestamps.
continue;
}
+ const uint32_t logged_channel_index =
+ configuration::ChannelIndex(configuration_, logged_channel);
+
FetcherStruct fs;
- fs.channel_index = channel_index;
- fs.channel = channel;
+ fs.logged_channel_index = logged_channel_index;
+ fs.event_loop_channel = event_loop_channel;
+
+ event_loop_to_logged_channel_index_[event_loop_channel_index] =
+ logged_channel_index;
const bool is_local =
- configuration::ChannelIsSendableOnNode(config_channel, node_);
+ configuration::ChannelIsSendableOnNode(logged_channel, node_);
const bool is_readable =
- configuration::ChannelIsReadableOnNode(config_channel, node_);
+ configuration::ChannelIsReadableOnNode(logged_channel, node_);
const bool is_logged =
- configuration::ChannelMessageIsLoggedOnNode(config_channel, node_);
+ configuration::ChannelMessageIsLoggedOnNode(logged_channel, node_);
const bool log_message = is_logged && is_readable;
bool log_delivery_times = false;
if (configuration::NodesCount(configuration_) > 1u) {
const aos::Connection *connection =
- configuration::ConnectionToNode(config_channel, node_);
+ configuration::ConnectionToNode(logged_channel, node_);
log_delivery_times = configuration::ConnectionDeliveryTimeIsLoggedOnNode(
connection, event_loop_->node());
CHECK_EQ(log_delivery_times,
configuration::ConnectionDeliveryTimeIsLoggedOnNode(
- config_channel, node_, node_));
+ logged_channel, node_, node_));
if (connection) {
fs.reliable_forwarding = (connection->time_to_live() == 0);
@@ -169,13 +179,14 @@
// Now, detect a RemoteMessage timestamp logger where we should just log
// the contents to a file directly.
- const bool log_contents = timestamp_logger_channels.find(channel) !=
- timestamp_logger_channels.end();
+ const bool log_contents =
+ timestamp_logger_channels.find(event_loop_channel) !=
+ timestamp_logger_channels.end();
if (log_message || log_delivery_times || log_contents) {
- fs.fetcher = event_loop->MakeRawFetcher(channel);
+ fs.fetcher = event_loop->MakeRawFetcher(event_loop_channel);
VLOG(1) << "Logging channel "
- << configuration::CleanedChannelToString(channel);
+ << configuration::CleanedChannelToString(event_loop_channel);
if (log_delivery_times) {
VLOG(1) << " Delivery times";
@@ -187,7 +198,7 @@
if (log_message || log_delivery_times) {
if (!is_local) {
const Node *source_node = configuration::GetNode(
- configuration_, channel->source_node()->string_view());
+ configuration_, event_loop_channel->source_node()->string_view());
fs.data_node_index =
configuration::GetNodeIndex(configuration_, source_node);
}
@@ -203,9 +214,9 @@
}
if (log_contents) {
VLOG(1) << "Timestamp logger channel "
- << configuration::CleanedChannelToString(channel);
+ << configuration::CleanedChannelToString(event_loop_channel);
auto timestamp_logger_channel_info =
- timestamp_logger_channels.find(channel);
+ timestamp_logger_channels.find(event_loop_channel);
CHECK(timestamp_logger_channel_info != timestamp_logger_channels.end());
fs.timestamp_node = std::get<0>(timestamp_logger_channel_info->second);
fs.reliable_contents =
@@ -276,14 +287,15 @@
for (FetcherStruct &f : fetchers_) {
if (f.wants_writer) {
- f.writer = log_namer_->MakeWriter(f.channel);
+ f.writer = log_namer_->MakeWriter(f.event_loop_channel);
}
if (f.wants_timestamp_writer) {
- f.timestamp_writer = log_namer_->MakeTimestampWriter(f.channel);
+ f.timestamp_writer =
+ log_namer_->MakeTimestampWriter(f.event_loop_channel);
}
if (f.wants_contents_writer) {
f.contents_writer = log_namer_->MakeForwardedTimestampWriter(
- f.channel, CHECK_NOTNULL(f.timestamp_node));
+ f.event_loop_channel, CHECK_NOTNULL(f.timestamp_node));
}
}
@@ -404,14 +416,15 @@
// Create writers from the new namer
if (f.wants_writer) {
- f.writer = log_namer_->MakeWriter(f.channel);
+ f.writer = log_namer_->MakeWriter(f.event_loop_channel);
}
if (f.wants_timestamp_writer) {
- f.timestamp_writer = log_namer_->MakeTimestampWriter(f.channel);
+ f.timestamp_writer =
+ log_namer_->MakeTimestampWriter(f.event_loop_channel);
}
if (f.wants_contents_writer) {
f.contents_writer = log_namer_->MakeForwardedTimestampWriter(
- f.channel, CHECK_NOTNULL(f.timestamp_node));
+ f.event_loop_channel, CHECK_NOTNULL(f.timestamp_node));
}
// Mark each channel with data as not written. That triggers each channel
@@ -702,8 +715,8 @@
// Write!
const auto start = event_loop_->monotonic_now();
- ContextDataCopier coppier(f.fetcher->context(), f.channel_index, f.log_type,
- event_loop_);
+ ContextDataCopier coppier(f.fetcher->context(), f.logged_channel_index,
+ f.log_type, event_loop_);
aos::monotonic_clock::time_point message_time =
static_cast<int>(node_index_) != f.data_node_index
@@ -729,10 +742,11 @@
timestamp_writer->UpdateRemote(
f.data_node_index, f.fetcher->context().source_boot_uuid,
f.fetcher->context().monotonic_remote_time,
+ f.fetcher->context().monotonic_remote_transmit_time,
f.fetcher->context().monotonic_event_time, f.reliable_forwarding);
const auto start = event_loop_->monotonic_now();
- ContextDataCopier coppier(f.fetcher->context(), f.channel_index,
+ ContextDataCopier coppier(f.fetcher->context(), f.logged_channel_index,
LogType::kLogDeliveryTimeOnly, event_loop_);
timestamp_writer->CopyTimestampMessage(
@@ -759,9 +773,11 @@
CHECK(msg->has_boot_uuid()) << ": " << aos::FlatbufferToJson(msg);
// Translate from the channel index that the event loop uses to the
// channel index in the log file.
- const int channel_index =
+ const std::optional<uint32_t> channel_index =
event_loop_to_logged_channel_index_[msg->channel_index()];
+ CHECK(channel_index.has_value());
+
const aos::monotonic_clock::time_point monotonic_timestamp_time =
f.fetcher->context().monotonic_event_time;
@@ -781,11 +797,13 @@
monotonic_clock::time_point(
chrono::nanoseconds(msg->monotonic_remote_time())),
monotonic_clock::time_point(
+ chrono::nanoseconds(msg->monotonic_remote_transmit_time())),
+ monotonic_clock::time_point(
chrono::nanoseconds(msg->monotonic_sent_time())),
reliable, monotonic_timestamp_time);
- RemoteMessageCopier coppier(msg, channel_index, monotonic_timestamp_time,
- event_loop_);
+ RemoteMessageCopier coppier(msg, channel_index.value(),
+ monotonic_timestamp_time, event_loop_);
contents_writer->CopyRemoteTimestampMessage(
&coppier, UUID::FromVector(msg->boot_uuid()), start,
@@ -896,7 +914,7 @@
total_message_fetch_time_ += duration;
if (duration > max_message_fetch_time_) {
max_message_fetch_time_ = duration;
- max_message_fetch_time_channel_ = fetcher->channel_index;
+ max_message_fetch_time_channel_ = fetcher->logged_channel_index;
max_message_fetch_time_size_ = fetcher->fetcher->context().size;
}
}
@@ -910,13 +928,13 @@
total_copy_bytes_ += fetcher.fetcher->context().size;
if (duration > max_copy_time_) {
max_copy_time_ = duration;
- max_copy_time_channel_ = fetcher.channel_index;
+ max_copy_time_channel_ = fetcher.logged_channel_index;
max_copy_time_size_ = fetcher.fetcher->context().size;
}
const auto log_delay = end - fetcher.fetcher->context().monotonic_event_time;
if (log_delay > max_log_delay_) {
max_log_delay_ = log_delay;
- max_log_delay_channel_ = fetcher.channel_index;
+ max_log_delay_channel_ = fetcher.logged_channel_index;
}
}
diff --git a/aos/events/logging/log_writer.h b/aos/events/logging/log_writer.h
index eb69d7e..6091d7e 100644
--- a/aos/events/logging/log_writer.h
+++ b/aos/events/logging/log_writer.h
@@ -203,9 +203,13 @@
std::unique_ptr<RawFetcher> fetcher;
bool written = false;
- // Channel index to log to.
- int channel_index = -1;
- const Channel *channel = nullptr;
+ // Index of the channel in the logged configuration (not necessarily the
+ // event loop configuration).
+ int logged_channel_index = -1;
+
+ // Channel from the event_loop configuration.
+ const Channel *event_loop_channel = nullptr;
+
const Node *timestamp_node = nullptr;
LogType log_type = LogType::kLogMessage;
@@ -245,7 +249,13 @@
// Vector mapping from the channel index from the event loop to the logged
// channel index.
- std::vector<int> event_loop_to_logged_channel_index_;
+ // When using the constructor that allows manually specifying the
+ // configuration, that configuration may have different channels than the
+ // event loop's configuration. When there is a channel that is included in the
+ // event loop configuration but not in the specified configuration, the value
+ // in this mapping will be nullopt for that channel. Nullopt will result in
+ // that channel not being included in the output log's configuration or data.
+ std::vector<std::optional<uint32_t>> event_loop_to_logged_channel_index_;
// Start/Restart write configuration into LogNamer space.
std::string WriteConfiguration(LogNamer *log_namer);
diff --git a/aos/events/logging/logfile_sorting.cc b/aos/events/logging/logfile_sorting.cc
index 2eaf00b..4c859c2 100644
--- a/aos/events/logging/logfile_sorting.cc
+++ b/aos/events/logging/logfile_sorting.cc
@@ -48,7 +48,7 @@
}
bool ConfigOnly(const LogFileHeader *header) {
- CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 35u);
+ CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 37u);
if (header->has_monotonic_start_time()) return false;
if (header->has_realtime_start_time()) return false;
if (header->has_max_out_of_order_duration()) return false;
@@ -74,6 +74,10 @@
if (header->has_oldest_local_unreliable_monotonic_timestamps()) return false;
if (header->has_oldest_remote_reliable_monotonic_timestamps()) return false;
if (header->has_oldest_local_reliable_monotonic_timestamps()) return false;
+ if (header->has_oldest_remote_reliable_monotonic_transmit_timestamps())
+ return false;
+ if (header->has_oldest_local_reliable_monotonic_transmit_timestamps())
+ return false;
if (header->has_oldest_logger_remote_unreliable_monotonic_timestamps())
return false;
if (header->has_oldest_logger_local_unreliable_monotonic_timestamps())
@@ -711,17 +715,55 @@
chrono::nanoseconds(
log_header->message().oldest_remote_monotonic_timestamps()->Get(
node_index)));
+
const monotonic_clock::time_point
- oldest_local_unreliable_monotonic_timestamp(chrono::nanoseconds(
+ oldest_local_reliable_monotonic_transmit_timestamp =
+ log_header->message()
+ .has_oldest_local_reliable_monotonic_transmit_timestamps()
+ ? monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_local_reliable_monotonic_transmit_timestamps()
+ ->Get(node_index)))
+ : monotonic_clock::max_time;
+ const monotonic_clock::time_point
+ oldest_remote_reliable_monotonic_transmit_timestamp =
+ log_header->message()
+ .has_oldest_remote_reliable_monotonic_transmit_timestamps()
+ ? monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_remote_reliable_monotonic_transmit_timestamps()
+ ->Get(node_index)))
+ : monotonic_clock::max_time;
+ monotonic_clock::time_point oldest_local_unreliable_monotonic_timestamp(
+ chrono::nanoseconds(
log_header->message()
.oldest_local_unreliable_monotonic_timestamps()
->Get(node_index)));
- const monotonic_clock::time_point
+ monotonic_clock::time_point
oldest_remote_unreliable_monotonic_timestamp(chrono::nanoseconds(
log_header->message()
.oldest_remote_unreliable_monotonic_timestamps()
->Get(node_index)));
+ // Treat transmit timestamps like unreliable timestamps. Update
+ // oldest_remote_unreliable_monotonic_timestamp accordingly to keep the
+ // logic after it simple and similar.
+ if (oldest_remote_reliable_monotonic_transmit_timestamp <
+ oldest_remote_unreliable_monotonic_timestamp) {
+ VLOG(1)
+ << "Updating oldest_remote_unreliable_monotonic_timestamp from "
+ << oldest_remote_unreliable_monotonic_timestamp << " to "
+ << oldest_remote_reliable_monotonic_transmit_timestamp
+ << " and oldest_local_unreliable_monotonic_timestamp from "
+ << oldest_local_unreliable_monotonic_timestamp << " to "
+ << oldest_local_reliable_monotonic_transmit_timestamp;
+
+ oldest_remote_unreliable_monotonic_timestamp =
+ oldest_remote_reliable_monotonic_transmit_timestamp;
+ oldest_local_unreliable_monotonic_timestamp =
+ oldest_local_reliable_monotonic_transmit_timestamp;
+ }
+
const monotonic_clock::time_point
oldest_logger_local_unreliable_monotonic_timestamp =
log_header->message()
diff --git a/aos/events/logging/logfile_utils.cc b/aos/events/logging/logfile_utils.cc
index 0166b72..0676577 100644
--- a/aos/events/logging/logfile_utils.cc
+++ b/aos/events/logging/logfile_utils.cc
@@ -316,14 +316,17 @@
message_header_builder.add_monotonic_sent_time(msg->monotonic_sent_time());
message_header_builder.add_realtime_sent_time(msg->realtime_sent_time());
+ message_header_builder.add_monotonic_timestamp_time(
+ monotonic_timestamp_time.time_since_epoch().count());
+
+ message_header_builder.add_monotonic_remote_transmit_time(
+ msg->monotonic_remote_transmit_time());
+
message_header_builder.add_monotonic_remote_time(
msg->monotonic_remote_time());
message_header_builder.add_realtime_remote_time(msg->realtime_remote_time());
message_header_builder.add_remote_queue_index(msg->remote_queue_index());
- message_header_builder.add_monotonic_timestamp_time(
- monotonic_timestamp_time.time_since_epoch().count());
-
return message_header_builder.Finish();
}
@@ -345,119 +348,116 @@
}
// clang-format off
// header:
- // +0x00 | 5C 00 00 00 | UOffset32 | 0x0000005C (92) Loc: +0x5C | size prefix
+ // +0x00 | 5C 00 00 00 | UOffset32 | 0x0000005C (92) Loc: +0x5C | size prefix
+
buffer = Push<flatbuffers::uoffset_t>(
buffer, message_size - sizeof(flatbuffers::uoffset_t));
- // +0x04 | 20 00 00 00 | UOffset32 | 0x00000020 (32) Loc: +0x24 | offset to root table `aos.logger.MessageHeader`
- buffer = Push<flatbuffers::uoffset_t>(buffer, 0x20);
+ // +0x04 | 1C 00 00 00 | UOffset32 | 0x0000001C (28) Loc: +0x20 | offset to root table `aos.logger.MessageHeader`
+ buffer = Push<flatbuffers::uoffset_t>(buffer, 0x1C);
[[fallthrough]];
case 0x08u:
if ((end_byte) == 0x08u) {
break;
}
//
- // padding:
- // +0x08 | 00 00 00 00 00 00 | uint8_t[6] | ...... | padding
- buffer = Pad(buffer, 6);
- //
// vtable (aos.logger.MessageHeader):
- // +0x0E | 16 00 | uint16_t | 0x0016 (22) | size of this vtable
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x16);
+ // +0x08 | 18 00 | uint16_t | 0x0018 (24) | size of this vtable
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x18);
+ // +0x0A | 40 00 | uint16_t | 0x0040 (64) | size of referring table
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x40);
+ // +0x0C | 3C 00 | VOffset16 | 0x003C (60) | offset to field `channel_index` (id: 0)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x3c);
+ // +0x0E | 30 00 | VOffset16 | 0x0030 (48) | offset to field `monotonic_sent_time` (id: 1)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x30);
[[fallthrough]];
case 0x10u:
if ((end_byte) == 0x10u) {
break;
}
- // +0x10 | 3C 00 | uint16_t | 0x003C (60) | size of referring table
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x3c);
- // +0x12 | 38 00 | VOffset16 | 0x0038 (56) | offset to field `channel_index` (id: 0)
+ // +0x10 | 28 00 | VOffset16 | 0x0028 (40) | offset to field `realtime_sent_time` (id: 2)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x28);
+ // +0x12 | 38 00 | VOffset16 | 0x0038 (56) | offset to field `queue_index` (id: 3)
buffer = Push<flatbuffers::voffset_t>(buffer, 0x38);
- // +0x14 | 2C 00 | VOffset16 | 0x002C (44) | offset to field `monotonic_sent_time` (id: 1)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x2c);
- // +0x16 | 24 00 | VOffset16 | 0x0024 (36) | offset to field `realtime_sent_time` (id: 2)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x24);
+ // +0x14 | 00 00 | VOffset16 | 0x0000 (0) | offset to field `data` (id: 4) <null> (Vector)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x00);
+ // +0x16 | 10 00 | VOffset16 | 0x0010 (16) | offset to field `monotonic_remote_time` (id: 5)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x10);
[[fallthrough]];
case 0x18u:
if ((end_byte) == 0x18u) {
break;
}
- // +0x18 | 34 00 | VOffset16 | 0x0034 (52) | offset to field `queue_index` (id: 3)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x34);
- // +0x1A | 00 00 | VOffset16 | 0x0000 (0) | offset to field `data` (id: 4) <null> (Vector)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x00);
- // +0x1C | 1C 00 | VOffset16 | 0x001C (28) | offset to field `monotonic_remote_time` (id: 5)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x1c);
- // +0x1E | 14 00 | VOffset16 | 0x0014 (20) | offset to field `realtime_remote_time` (id: 6)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x14);
+ // +0x18 | 08 00 | VOffset16 | 0x0008 (8) | offset to field `realtime_remote_time` (id: 6)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x08);
+ // +0x1A | 04 00 | VOffset16 | 0x0004 (4) | offset to field `remote_queue_index` (id: 7)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x04);
+ // +0x1C | 20 00 | VOffset16 | 0x0020 (32) | offset to field `monotonic_timestamp_time` (id: 8)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x20);
+ // +0x1E | 18 00 | VOffset16 | 0x0018 (24) | offset to field `monotonic_remote_transmit_time` (id: 9)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x18);
[[fallthrough]];
case 0x20u:
if ((end_byte) == 0x20u) {
break;
}
- // +0x20 | 10 00 | VOffset16 | 0x0010 (16) | offset to field `remote_queue_index` (id: 7)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x10);
- // +0x22 | 04 00 | VOffset16 | 0x0004 (4) | offset to field `monotonic_timestamp_time` (id: 8)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x04);
- //
+
+
// root_table (aos.logger.MessageHeader):
- // +0x24 | 16 00 00 00 | SOffset32 | 0x00000016 (22) Loc: +0x0E | offset to vtable
- buffer = Push<flatbuffers::uoffset_t>(buffer, 0x16);
+ // +0x20 | 18 00 00 00 | SOffset32 | 0x00000018 (24) Loc: +0x08 | offset to vtable
+ buffer = Push<flatbuffers::uoffset_t>(buffer, 0x18);
+ // +0x24 | 8B 00 00 00 | uint32_t | 0x0000008B (139) | table field `remote_queue_index` (UInt)
+ buffer = Push<uint32_t>(buffer, msg->remote_queue_index());
[[fallthrough]];
case 0x28u:
if ((end_byte) == 0x28u) {
break;
}
- // +0x28 | F6 0B D8 11 A4 A8 B1 71 | int64_t | 0x71B1A8A411D80BF6 (8192514619791117302) | table field `monotonic_timestamp_time` (Long)
- buffer = Push<int64_t>(buffer,
- monotonic_timestamp_time.time_since_epoch().count());
+ // +0x28 | D4 C9 48 86 92 8B 6A AF | int64_t | 0xAF6A8B928648C9D4 (-5806675308106429996) | table field `realtime_remote_time` (Long)
+ buffer = Push<int64_t>(buffer, msg->realtime_remote_time());
[[fallthrough]];
case 0x30u:
if ((end_byte) == 0x30u) {
break;
}
- // +0x30 | 00 00 00 00 | uint8_t[4] | .... | padding
- // TODO(austin): Can we re-arrange the order to ditch the padding?
- // (Answer is yes, but what is the impact elsewhere? It will change the
- // binary format)
- buffer = Pad(buffer, 4);
- // +0x34 | 75 00 00 00 | uint32_t | 0x00000075 (117) | table field `remote_queue_index` (UInt)
- buffer = Push<uint32_t>(buffer, msg->remote_queue_index());
+ // +0x30 | 65 B1 32 50 FE 54 50 6B | int64_t | 0x6B5054FE5032B165 (7732774011439067493) | table field `monotonic_remote_time` (Long)
+ buffer = Push<int64_t>(buffer, msg->monotonic_remote_time());
[[fallthrough]];
case 0x38u:
if ((end_byte) == 0x38u) {
break;
}
- // +0x38 | AA B0 43 0A 35 BE FA D2 | int64_t | 0xD2FABE350A43B0AA (-3244071446552268630) | table field `realtime_remote_time` (Long)
- buffer = Push<int64_t>(buffer, msg->realtime_remote_time());
+ // +0x38 | EA 4D CC E0 FC 20 86 71 | int64_t | 0x718620FCE0CC4DEA (8180262043640417770) | table field `monotonic_remote_transmit_time` (Long)
+ buffer = Push<int64_t>(buffer, msg->monotonic_remote_transmit_time());
[[fallthrough]];
case 0x40u:
if ((end_byte) == 0x40u) {
break;
}
- // +0x40 | D5 40 30 F3 C1 A7 26 1D | int64_t | 0x1D26A7C1F33040D5 (2100550727665467605) | table field `monotonic_remote_time` (Long)
- buffer = Push<int64_t>(buffer, msg->monotonic_remote_time());
+ // +0x40 | 8E 59 CF 88 9D DF 02 07 | int64_t | 0x0702DF9D88CF598E (505211975917066638) | table field `monotonic_timestamp_time` (Long)
+ buffer = Push<int64_t>(buffer,
+ monotonic_timestamp_time.time_since_epoch().count());
[[fallthrough]];
case 0x48u:
if ((end_byte) == 0x48u) {
break;
}
- // +0x48 | 5B 25 32 A1 4A E8 46 CA | int64_t | 0xCA46E84AA132255B (-3871151422448720549) | table field `realtime_sent_time` (Long)
+ // +0x48 | 14 D5 A7 D8 B2 E4 EF 89 | int64_t | 0x89EFE4B2D8A7D514 (-8507329714289388268) | table field `realtime_sent_time` (Long)
buffer = Push<int64_t>(buffer, msg->realtime_sent_time());
[[fallthrough]];
case 0x50u:
if ((end_byte) == 0x50u) {
break;
}
- // +0x50 | 49 7D 45 1F 8C 36 6B A3 | int64_t | 0xA36B368C1F457D49 (-6671178447571288759) | table field `monotonic_sent_time` (Long)
+ // +0x50 | 19 7D 7F EF 86 8D 92 65 | int64_t | 0x65928D86EF7F7D19 (7319067955113721113) | table field `monotonic_sent_time` (Long)
buffer = Push<int64_t>(buffer, msg->monotonic_sent_time());
[[fallthrough]];
case 0x58u:
if ((end_byte) == 0x58u) {
break;
}
- // +0x58 | 33 00 00 00 | uint32_t | 0x00000033 (51) | table field `queue_index` (UInt)
+ // +0x58 | FC 00 00 00 | uint32_t | 0x000000FC (252) | table field `queue_index` (UInt)
buffer = Push<uint32_t>(buffer, msg->queue_index());
- // +0x5C | 76 00 00 00 | uint32_t | 0x00000076 (118) | table field `channel_index` (UInt)
+ // +0x5C | 9C 00 00 00 | uint32_t | 0x0000009C (156) | table field `channel_index` (UInt)
buffer = Push<uint32_t>(buffer, channel_index);
// clang-format on
[[fallthrough]];
@@ -477,7 +477,6 @@
switch (log_type) {
case LogType::kLogMessage:
- case LogType::kLogMessageAndDeliveryTime:
case LogType::kLogRemoteMessage:
// Since the timestamps are 8 byte aligned, we are going to end up adding
// padding in the middle of the message to pad everything out to 8 byte
@@ -525,6 +524,8 @@
context.monotonic_remote_time.time_since_epoch().count());
message_header_builder.add_realtime_remote_time(
context.realtime_remote_time.time_since_epoch().count());
+ message_header_builder.add_monotonic_remote_transmit_time(
+ context.monotonic_remote_transmit_time.time_since_epoch().count());
message_header_builder.add_remote_queue_index(context.remote_queue_index);
break;
@@ -536,20 +537,6 @@
message_header_builder.add_realtime_sent_time(
context.realtime_event_time.time_since_epoch().count());
break;
-
- case LogType::kLogMessageAndDeliveryTime:
- message_header_builder.add_queue_index(context.queue_index);
- message_header_builder.add_remote_queue_index(context.remote_queue_index);
- message_header_builder.add_monotonic_sent_time(
- context.monotonic_event_time.time_since_epoch().count());
- message_header_builder.add_realtime_sent_time(
- context.realtime_event_time.time_since_epoch().count());
- message_header_builder.add_monotonic_remote_time(
- context.monotonic_remote_time.time_since_epoch().count());
- message_header_builder.add_realtime_remote_time(
- context.realtime_remote_time.time_since_epoch().count());
- message_header_builder.add_data(data_offset);
- break;
}
return message_header_builder.Finish();
@@ -580,42 +567,20 @@
return
// Root table size + offset.
sizeof(flatbuffers::uoffset_t) * 2 +
- // 6 padding bytes to pad the header out properly.
- 4 +
// vtable header (size + size of table)
sizeof(flatbuffers::voffset_t) * 2 +
// offsets to all the fields.
- sizeof(flatbuffers::voffset_t) * 8 +
+ sizeof(flatbuffers::voffset_t) * 10 +
// pointer to vtable
sizeof(flatbuffers::soffset_t) +
// remote_queue_index
sizeof(uint32_t) +
// realtime_remote_time, monotonic_remote_time, realtime_sent_time,
// monotonic_sent_time
- sizeof(int64_t) * 4 +
+ sizeof(int64_t) * 5 +
// queue_index, channel_index
sizeof(uint32_t) * 2;
- case LogType::kLogMessageAndDeliveryTime:
- return
- // Root table size + offset.
- sizeof(flatbuffers::uoffset_t) * 2 +
- // 4 padding bytes to pad the header out properly.
- 4 +
- // vtable header (size + size of table)
- sizeof(flatbuffers::voffset_t) * 2 +
- // offsets to all the fields.
- sizeof(flatbuffers::voffset_t) * 8 +
- // pointer to vtable
- sizeof(flatbuffers::soffset_t) +
- // pointer to data
- sizeof(flatbuffers::uoffset_t) +
- // realtime_remote_time, monotonic_remote_time, realtime_sent_time,
- // monotonic_sent_time
- sizeof(int64_t) * 4 +
- // remote_queue_index, queue_index, channel_index
- sizeof(uint32_t) * 3;
-
case LogType::kLogRemoteMessage:
return
// Root table size + offset.
@@ -648,7 +613,6 @@
return PackMessageHeaderSize(log_type);
case LogType::kLogMessage:
- case LogType::kLogMessageAndDeliveryTime:
case LogType::kLogRemoteMessage:
return PackMessageHeaderSize(log_type) +
// Vector...
@@ -822,10 +786,10 @@
}
// clang-format off
// header:
- // +0x00 | 4C 00 00 00 | UOffset32 | 0x0000004C (76) Loc: +0x4C | size prefix
+ // +0x00 | 54 00 00 00 | UOffset32 | 0x00000054 (84) Loc: +0x54 | size prefix
buffer = Push<flatbuffers::uoffset_t>(
buffer, message_size - sizeof(flatbuffers::uoffset_t));
- // +0x04 | 1C 00 00 00 | UOffset32 | 0x0000001C (28) Loc: +0x20 | offset to root table `aos.logger.MessageHeader`
+ // +0x04 | 1C 00 00 00 | UOffset32 | 0x0000001C (28) Loc: +0x20 | offset to root table `aos.logger.MessageHeader`
buffer = Push<flatbuffers::uoffset_t>(buffer, 0x1c);
[[fallthrough]];
@@ -834,254 +798,98 @@
break;
}
//
- // padding:
- // +0x08 | 00 00 00 00 | uint8_t[4] | .... | padding
- buffer = Pad(buffer, 4);
- //
// vtable (aos.logger.MessageHeader):
- // +0x0C | 14 00 | uint16_t | 0x0014 (20) | size of this vtable
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x14);
- // +0x0E | 30 00 | uint16_t | 0x0030 (48) | size of referring table
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x30);
- [[fallthrough]];
- case 0x10u:
- if ((end_byte) == 0x10u) {
- break;
- }
- // +0x10 | 2C 00 | VOffset16 | 0x002C (44) | offset to field `channel_index` (id: 0)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x2c);
- // +0x12 | 20 00 | VOffset16 | 0x0020 (32) | offset to field `monotonic_sent_time` (id: 1)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x20);
- // +0x14 | 18 00 | VOffset16 | 0x0018 (24) | offset to field `realtime_sent_time` (id: 2)
+ // +0x08 | 18 00 | uint16_t | 0x0018 (24) | size of this vtable
buffer = Push<flatbuffers::voffset_t>(buffer, 0x18);
- // +0x16 | 28 00 | VOffset16 | 0x0028 (40) | offset to field `queue_index` (id: 3)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x28);
- [[fallthrough]];
- case 0x18u:
- if ((end_byte) == 0x18u) {
- break;
- }
- // +0x18 | 00 00 | VOffset16 | 0x0000 (0) | offset to field `data` (id: 4) <null> (Vector)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x00);
- // +0x1A | 10 00 | VOffset16 | 0x0010 (16) | offset to field `monotonic_remote_time` (id: 5)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x10);
- // +0x1C | 08 00 | VOffset16 | 0x0008 (8) | offset to field `realtime_remote_time` (id: 6)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x08);
- // +0x1E | 04 00 | VOffset16 | 0x0004 (4) | offset to field `remote_queue_index` (id: 7)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x04);
- [[fallthrough]];
- case 0x20u:
- if ((end_byte) == 0x20u) {
- break;
- }
- //
- // root_table (aos.logger.MessageHeader):
- // +0x20 | 14 00 00 00 | SOffset32 | 0x00000014 (20) Loc: +0x0C | offset to vtable
- buffer = Push<flatbuffers::uoffset_t>(buffer, 0x14);
- // +0x24 | 69 00 00 00 | uint32_t | 0x00000069 (105) | table field `remote_queue_index` (UInt)
- buffer = Push<uint32_t>(buffer, context.remote_queue_index);
- [[fallthrough]];
- case 0x28u:
- if ((end_byte) == 0x28u) {
- break;
- }
- // +0x28 | C6 85 F1 AB 83 B5 CD EB | int64_t | 0xEBCDB583ABF185C6 (-1455307527440726586) | table field `realtime_remote_time` (Long)
- buffer = Push<int64_t>(buffer, context.realtime_remote_time.time_since_epoch().count());
- [[fallthrough]];
- case 0x30u:
- if ((end_byte) == 0x30u) {
- break;
- }
- // +0x30 | 47 24 D3 97 1E 42 2D 99 | int64_t | 0x992D421E97D32447 (-7409193112790948793) | table field `monotonic_remote_time` (Long)
- buffer = Push<int64_t>(buffer, context.monotonic_remote_time.time_since_epoch().count());
- [[fallthrough]];
- case 0x38u:
- if ((end_byte) == 0x38u) {
- break;
- }
- // +0x38 | C8 B9 A7 AB 79 F2 CD 60 | int64_t | 0x60CDF279ABA7B9C8 (6975498002251626952) | table field `realtime_sent_time` (Long)
- buffer = Push<int64_t>(buffer, context.realtime_event_time.time_since_epoch().count());
- [[fallthrough]];
- case 0x40u:
- if ((end_byte) == 0x40u) {
- break;
- }
- // +0x40 | EA 8F 2A 0F AF 01 7A AB | int64_t | 0xAB7A01AF0F2A8FEA (-6090553694679822358) | table field `monotonic_sent_time` (Long)
- buffer = Push<int64_t>(buffer, context.monotonic_event_time.time_since_epoch().count());
- [[fallthrough]];
- case 0x48u:
- if ((end_byte) == 0x48u) {
- break;
- }
- // +0x48 | F5 00 00 00 | uint32_t | 0x000000F5 (245) | table field `queue_index` (UInt)
- buffer = Push<uint32_t>(buffer, context.queue_index);
- // +0x4C | 88 00 00 00 | uint32_t | 0x00000088 (136) | table field `channel_index` (UInt)
- buffer = Push<uint32_t>(buffer, channel_index);
-
- // clang-format on
- }
- break;
-
- case LogType::kLogMessageAndDeliveryTime:
- switch (start_byte) {
- case 0x00u:
- if ((end_byte) == 0x00u) {
- break;
- }
- // clang-format off
- // header:
- // +0x00 | 5C 00 00 00 | UOffset32 | 0x0000005C (92) Loc: +0x5C | size prefix
- buffer = Push<flatbuffers::uoffset_t>(
- buffer, message_size - sizeof(flatbuffers::uoffset_t));
- // +0x04 | 1C 00 00 00 | UOffset32 | 0x0000001C (28) Loc: +0x20 | offset to root table `aos.logger.MessageHeader`
- buffer = Push<flatbuffers::uoffset_t>(buffer, 0x1c);
- [[fallthrough]];
- case 0x08u:
- if ((end_byte) == 0x08u) {
- break;
- }
- //
- // padding:
- // +0x08 | 00 00 00 00 | uint8_t[4] | .... | padding
- buffer = Pad(buffer, 4);
- //
- // vtable (aos.logger.MessageHeader):
- // +0x0C | 14 00 | uint16_t | 0x0014 (20) | size of this vtable
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x14);
- // +0x0E | 34 00 | uint16_t | 0x0034 (52) | size of referring table
+ // +0x0A | 38 00 | uint16_t | 0x0038 (56) | size of referring table
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x38);
+ // +0x0C | 34 00 | VOffset16 | 0x0034 (52) | offset to field `channel_index` (id: 0)
buffer = Push<flatbuffers::voffset_t>(buffer, 0x34);
+ // +0x0E | 28 00 | VOffset16 | 0x0028 (40) | offset to field `monotonic_sent_time` (id: 1)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x28);
[[fallthrough]];
case 0x10u:
if ((end_byte) == 0x10u) {
break;
}
- // +0x10 | 30 00 | VOffset16 | 0x0030 (48) | offset to field `channel_index` (id: 0)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x30);
- // +0x12 | 20 00 | VOffset16 | 0x0020 (32) | offset to field `monotonic_sent_time` (id: 1)
+ // +0x10 | 20 00 | VOffset16 | 0x0020 (32) | offset to field `realtime_sent_time` (id: 2)
buffer = Push<flatbuffers::voffset_t>(buffer, 0x20);
- // +0x14 | 18 00 | VOffset16 | 0x0018 (24) | offset to field `realtime_sent_time` (id: 2)
+ // +0x12 | 30 00 | VOffset16 | 0x0030 (48) | offset to field `queue_index` (id: 3)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x30);
+ // +0x14 | 00 00 | VOffset16 | 0x0000 (0) | offset to field `data` (id: 4) <null> (Vector)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x00);
+ // +0x16 | 18 00 | VOffset16 | 0x0018 (24) | offset to field `monotonic_remote_time` (id: 5)
buffer = Push<flatbuffers::voffset_t>(buffer, 0x18);
- // +0x16 | 2C 00 | VOffset16 | 0x002C (44) | offset to field `queue_index` (id: 3)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x2c);
[[fallthrough]];
case 0x18u:
if ((end_byte) == 0x18u) {
break;
}
- // +0x18 | 04 00 | VOffset16 | 0x0004 (4) | offset to field `data` (id: 4)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x04);
- // +0x1A | 10 00 | VOffset16 | 0x0010 (16) | offset to field `monotonic_remote_time` (id: 5)
+ // +0x18 | 10 00 | VOffset16 | 0x0010 (16) | offset to field `realtime_remote_time` (id: 6)
buffer = Push<flatbuffers::voffset_t>(buffer, 0x10);
- // +0x1C | 08 00 | VOffset16 | 0x0008 (8) | offset to field `realtime_remote_time` (id: 6)
+ // +0x1A | 04 00 | VOffset16 | 0x0004 (4) | offset to field `remote_queue_index` (id: 7)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x04);
+ // +0x1C | 00 00 | VOffset16 | 0x0000 (0) | offset to field `monotonic_timestamp_time` (id: 8) <defaults to -9223372036854775808> (Long)
+ buffer = Push<flatbuffers::voffset_t>(buffer, 0x00);
+ // +0x1E | 08 00 | VOffset16 | 0x0008 (8) | offset to field `monotonic_remote_transmit_time` (id: 9)
buffer = Push<flatbuffers::voffset_t>(buffer, 0x08);
- // +0x1E | 28 00 | VOffset16 | 0x0028 (40) | offset to field `remote_queue_index` (id: 7)
- buffer = Push<flatbuffers::voffset_t>(buffer, 0x28);
[[fallthrough]];
case 0x20u:
if ((end_byte) == 0x20u) {
break;
}
- //
// root_table (aos.logger.MessageHeader):
- // +0x20 | 14 00 00 00 | SOffset32 | 0x00000014 (20) Loc: +0x0C | offset to vtable
- buffer = Push<flatbuffers::uoffset_t>(buffer, 0x14);
- // +0x24 | 30 00 00 00 | UOffset32 | 0x00000030 (48) Loc: +0x54 | offset to field `data` (vector)
- buffer = Push<flatbuffers::uoffset_t>(buffer, 0x30);
+ // +0x20 | 18 00 00 00 | SOffset32 | 0x00000018 (24) Loc: +0x08 | offset to vtable
+ buffer = Push<flatbuffers::uoffset_t>(buffer, 0x18);
+ // +0x24 | 3F 9A 69 37 | uint32_t | 0x37699A3F (929667647) | table field `remote_queue_index` (UInt)
+ buffer = Push<uint32_t>(buffer, context.remote_queue_index);
[[fallthrough]];
case 0x28u:
if ((end_byte) == 0x28u) {
break;
}
- // +0x28 | C4 C8 87 BF 40 6C 1F 29 | int64_t | 0x291F6C40BF87C8C4 (2963206105180129476) | table field `realtime_remote_time` (Long)
- buffer = Push<int64_t>(buffer, context.realtime_remote_time.time_since_epoch().count());
+ // +0x28 | 00 00 00 00 00 00 00 80 | int64_t | 0x8000000000000000 (-9223372036854775808) | table field `monotonic_remote_transmit_time` (Long)
+ buffer = Push<int64_t>(buffer, context.monotonic_remote_transmit_time.time_since_epoch().count());
[[fallthrough]];
case 0x30u:
if ((end_byte) == 0x30u) {
break;
}
- // +0x30 | 0F 00 26 FD D2 6D C0 1F | int64_t | 0x1FC06DD2FD26000F (2287949363661897743) | table field `monotonic_remote_time` (Long)
- buffer = Push<int64_t>(buffer, context.monotonic_remote_time.time_since_epoch().count());
+ // +0x30 | 1D CE 4A 38 54 33 C9 F8 | int64_t | 0xF8C93354384ACE1D (-519827845169885667) | table field `realtime_remote_time` (Long)
+ buffer = Push<int64_t>(buffer, context.realtime_remote_time.time_since_epoch().count());
[[fallthrough]];
case 0x38u:
if ((end_byte) == 0x38u) {
break;
}
- // +0x38 | 29 75 09 C0 73 73 BF 88 | int64_t | 0x88BF7373C0097529 (-8593022623019338455) | table field `realtime_sent_time` (Long)
- buffer = Push<int64_t>(buffer, context.realtime_event_time.time_since_epoch().count());
+ // +0x38 | FE EA DF 1D C7 3F C6 03 | int64_t | 0x03C63FC71DDFEAFE (271974951934749438) | table field `monotonic_remote_time` (Long)
+ buffer = Push<int64_t>(buffer, context.monotonic_remote_time.time_since_epoch().count());
[[fallthrough]];
case 0x40u:
if ((end_byte) == 0x40u) {
break;
}
- // +0x40 | 6D 8A AE 04 50 25 9C E9 | int64_t | 0xE99C255004AE8A6D (-1613373540899321235) | table field `monotonic_sent_time` (Long)
- buffer = Push<int64_t>(buffer, context.monotonic_event_time.time_since_epoch().count());
+ // +0x40 | 4E 0C 96 6E FB B5 CE 12 | int64_t | 0x12CEB5FB6E960C4E (1355220629381844046) | table field `realtime_sent_time` (Long)
+ buffer = Push<int64_t>(buffer, context.realtime_event_time.time_since_epoch().count());
[[fallthrough]];
case 0x48u:
if ((end_byte) == 0x48u) {
break;
}
- // +0x48 | 47 00 00 00 | uint32_t | 0x00000047 (71) | table field `remote_queue_index` (UInt)
- buffer = Push<uint32_t>(buffer, context.remote_queue_index);
- // +0x4C | 4C 00 00 00 | uint32_t | 0x0000004C (76) | table field `queue_index` (UInt)
- buffer = Push<uint32_t>(buffer, context.queue_index);
+ // +0x48 | 51 56 56 F9 0A 0B 0F 12 | int64_t | 0x120F0B0AF9565651 (1301270959094126161) | table field `monotonic_sent_time` (Long)
+ buffer = Push<int64_t>(buffer, context.monotonic_event_time.time_since_epoch().count());
[[fallthrough]];
case 0x50u:
if ((end_byte) == 0x50u) {
break;
}
- // +0x50 | 72 00 00 00 | uint32_t | 0x00000072 (114) | table field `channel_index` (UInt)
+ // +0x50 | 0C A5 42 18 | uint32_t | 0x1842A50C (407020812) | table field `queue_index` (UInt)
+ buffer = Push<uint32_t>(buffer, context.queue_index);
+ // +0x54 | 87 10 7C D7 | uint32_t | 0xD77C1087 (3615232135) | table field `channel_index` (UInt)
buffer = Push<uint32_t>(buffer, channel_index);
- //
- // vector (aos.logger.MessageHeader.data):
- // +0x54 | 07 00 00 00 | uint32_t | 0x00000007 (7) | length of vector (# items)
- buffer = Push<flatbuffers::uoffset_t>(buffer, context.size);
- [[fallthrough]];
- case 0x58u:
- if ((end_byte) == 0x58u) {
- break;
- }
- [[fallthrough]];
- default:
- // +0x58 | B1 | uint8_t | 0xB1 (177) | value[0]
- // +0x59 | 4A | uint8_t | 0x4A (74) | value[1]
- // +0x5A | 50 | uint8_t | 0x50 (80) | value[2]
- // +0x5B | 24 | uint8_t | 0x24 (36) | value[3]
- // +0x5C | AF | uint8_t | 0xAF (175) | value[4]
- // +0x5D | C8 | uint8_t | 0xC8 (200) | value[5]
- // +0x5E | D5 | uint8_t | 0xD5 (213) | value[6]
- //
- // padding:
- // +0x5F | 00 | uint8_t[1] | . | padding
+
// clang-format on
-
- if (start_byte <= 0x58 && end_byte == message_size) {
- // The easy one, slap it all down.
- buffer = PushBytes(buffer, context.data, context.size);
- buffer =
- Pad(buffer, ((context.size + 7) & 0xfffffff8u) - context.size);
- } else {
- const size_t data_start_byte =
- start_byte < 0x58 ? 0x0u : (start_byte - 0x58);
- const size_t data_end_byte = end_byte - 0x58;
- const size_t padded_size = ((context.size + 7) & 0xfffffff8u);
- if (data_start_byte < padded_size) {
- buffer = PushBytes(
- buffer,
- reinterpret_cast<const uint8_t *>(context.data) +
- data_start_byte,
- std::min(context.size, data_end_byte) - data_start_byte);
- if (data_end_byte == padded_size) {
- // We can only pad the last 7 bytes, so this only gets written
- // if we write the last byte.
- buffer = Pad(buffer,
- ((context.size + 7) & 0xfffffff8u) - context.size);
- }
- }
- }
-
- break;
}
-
break;
case LogType::kLogRemoteMessage:
@@ -1559,6 +1367,9 @@
realtime_remote_time = realtime_clock::time_point(
chrono::nanoseconds(message.realtime_remote_time()));
}
+ aos::monotonic_clock::time_point monotonic_remote_transmit_time =
+ aos::monotonic_clock::time_point(
+ std::chrono::nanoseconds(message.monotonic_remote_transmit_time()));
std::optional<uint32_t> remote_queue_index;
if (message.has_remote_queue_index()) {
@@ -1572,7 +1383,7 @@
realtime_clock::time_point(
chrono::nanoseconds(message.realtime_sent_time())),
message.queue_index(), monotonic_remote_time, realtime_remote_time,
- remote_queue_index,
+ monotonic_remote_transmit_time, remote_queue_index,
monotonic_clock::time_point(
std::chrono::nanoseconds(message.monotonic_timestamp_time())),
message.has_monotonic_timestamp_time(), span);
@@ -1780,6 +1591,10 @@
if (msg.realtime_remote_time != realtime_clock::min_time) {
os << ", .realtime_remote_time=" << msg.realtime_remote_time;
}
+ if (msg.monotonic_remote_transmit_time != BootTimestamp::min_time()) {
+ os << ", .monotonic_remote_transmit_time="
+ << msg.monotonic_remote_transmit_time;
+ }
if (msg.monotonic_timestamp_time != BootTimestamp::min_time()) {
os << ", .monotonic_timestamp_time=" << msg.monotonic_timestamp_time;
}
@@ -1824,6 +1639,7 @@
size_t monotonic_remote_boot = 0xffffff;
if (msg->monotonic_remote_time.has_value()) {
+ CHECK_LT(msg->channel_index, source_node_index_.size());
const Node *node = parts().config->nodes()->Get(
source_node_index_[msg->channel_index]);
@@ -2204,6 +2020,7 @@
queue_timestamps_ran_ = true;
return;
}
+ CHECK_LT(msg->channel_index, source_node.size());
if (source_node[msg->channel_index] != static_cast<size_t>(node())) {
timestamp_messages_.emplace_back(TimestampedMessage{
.channel_index = msg->channel_index,
@@ -2216,6 +2033,9 @@
.monotonic_remote_time = {msg->monotonic_remote_boot,
msg->data->monotonic_remote_time.value()},
.realtime_remote_time = msg->data->realtime_remote_time.value(),
+ .monotonic_remote_transmit_time =
+ {msg->monotonic_remote_boot,
+ msg->data->monotonic_remote_transmit_time},
.monotonic_timestamp_time = {msg->monotonic_timestamp_boot,
msg->data->monotonic_timestamp_time},
.data = std::move(msg->data)});
@@ -2381,6 +2201,9 @@
source_node_.emplace_back(configuration::GetNodeIndex(
config, channel->source_node()->string_view()));
}
+ } else {
+ // The node index for single-node logs is always 0.
+ source_node_.resize(config->channels()->size(), 0);
}
}
@@ -2404,16 +2227,17 @@
}
void TimestampMapper::QueueMessage(const Message *msg) {
- matched_messages_.emplace_back(
- TimestampedMessage{.channel_index = msg->channel_index,
- .queue_index = msg->queue_index,
- .monotonic_event_time = msg->timestamp,
- .realtime_event_time = msg->data->realtime_sent_time,
- .remote_queue_index = BootQueueIndex::Invalid(),
- .monotonic_remote_time = BootTimestamp::min_time(),
- .realtime_remote_time = realtime_clock::min_time,
- .monotonic_timestamp_time = BootTimestamp::min_time(),
- .data = std::move(msg->data)});
+ matched_messages_.emplace_back(TimestampedMessage{
+ .channel_index = msg->channel_index,
+ .queue_index = msg->queue_index,
+ .monotonic_event_time = msg->timestamp,
+ .realtime_event_time = msg->data->realtime_sent_time,
+ .remote_queue_index = BootQueueIndex::Invalid(),
+ .monotonic_remote_time = BootTimestamp::min_time(),
+ .realtime_remote_time = realtime_clock::min_time,
+ .monotonic_remote_transmit_time = BootTimestamp::min_time(),
+ .monotonic_timestamp_time = BootTimestamp::min_time(),
+ .data = std::move(msg->data)});
VLOG(1) << node_name() << " Inserted " << matched_messages_.back();
}
@@ -2536,6 +2360,9 @@
.monotonic_remote_time = {msg->monotonic_remote_boot,
msg->data->monotonic_remote_time.value()},
.realtime_remote_time = msg->data->realtime_remote_time.value(),
+ .monotonic_remote_transmit_time =
+ {msg->monotonic_remote_boot,
+ msg->data->monotonic_remote_transmit_time},
.monotonic_timestamp_time = {msg->monotonic_timestamp_boot,
msg->data->monotonic_timestamp_time},
.data = std::move(data.data)});
diff --git a/aos/events/logging/logfile_utils.h b/aos/events/logging/logfile_utils.h
index 8a569ba..d781a20 100644
--- a/aos/events/logging/logfile_utils.h
+++ b/aos/events/logging/logfile_utils.h
@@ -37,10 +37,6 @@
// The message originated on another node, but only the delivery times are
// logged here.
kLogDeliveryTimeOnly,
- // The message originated on another node. Log it and the delivery times
- // together. The message_gateway is responsible for logging any messages
- // which didn't get delivered.
- kLogMessageAndDeliveryTime,
// The message originated on the other node and should be logged on this node.
kLogRemoteMessage
};
@@ -445,6 +441,7 @@
realtime_clock::time_point realtime_sent_time, uint32_t queue_index,
std::optional<monotonic_clock::time_point> monotonic_remote_time,
std::optional<realtime_clock::time_point> realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
std::optional<uint32_t> remote_queue_index,
monotonic_clock::time_point monotonic_timestamp_time,
bool has_monotonic_timestamp_time, absl::Span<const uint8_t> span)
@@ -454,6 +451,7 @@
queue_index(queue_index),
monotonic_remote_time(monotonic_remote_time),
realtime_remote_time(realtime_remote_time),
+ monotonic_remote_transmit_time(monotonic_remote_transmit_time),
remote_queue_index(remote_queue_index),
monotonic_timestamp_time(monotonic_timestamp_time),
has_monotonic_timestamp_time(has_monotonic_timestamp_time),
@@ -473,6 +471,7 @@
std::optional<aos::monotonic_clock::time_point> monotonic_remote_time;
std::optional<realtime_clock::time_point> realtime_remote_time;
+ aos::monotonic_clock::time_point monotonic_remote_transmit_time;
std::optional<uint32_t> remote_queue_index;
// This field is defaulted in the flatbuffer, so we need to store both the
@@ -550,6 +549,8 @@
BootTimestamp monotonic_remote_time;
realtime_clock::time_point realtime_remote_time = realtime_clock::min_time;
+ BootTimestamp monotonic_remote_transmit_time;
+
BootTimestamp monotonic_timestamp_time;
std::shared_ptr<UnpackedMessageHeader> data;
diff --git a/aos/events/logging/logfile_utils_test.cc b/aos/events/logging/logfile_utils_test.cc
index 30a0ada..4ceca20 100644
--- a/aos/events/logging/logfile_utils_test.cc
+++ b/aos/events/logging/logfile_utils_test.cc
@@ -326,11 +326,44 @@
return fbb2.Release();
}
+// Allows for some customization of a SortingElementTest.
+enum class SortingElementConfig {
+ // Create a single node configuration.
+ kSingleNode,
+ // Create a multi-node configuration.
+ kMultiNode,
+};
+
+template <SortingElementConfig sorting_element_config =
+ SortingElementConfig::kMultiNode>
class SortingElementTest : public ::testing::Test {
public:
SortingElementTest()
: config_(JsonToFlatbuffer<Configuration>(
- R"({
+ sorting_element_config == SortingElementConfig::kSingleNode ?
+ R"({
+ "channels": [
+ {
+ "name": "/a",
+ "type": "aos.logger.testing.TestMessage"
+ },
+ {
+ "name": "/b",
+ "type": "aos.logger.testing.TestMessage"
+ },
+ {
+ "name": "/c",
+ "type": "aos.logger.testing.TestMessage"
+ },
+ {
+ "name": "/d",
+ "type": "aos.logger.testing.TestMessage"
+ }
+ ]
+}
+)"
+ :
+ R"({
"channels": [
{
"name": "/a",
@@ -379,7 +412,31 @@
]
}
)")),
- config0_(MakeHeader(config_, R"({
+ config0_(MakeHeader(
+ config_, sorting_element_config == SortingElementConfig::kSingleNode
+ ?
+ R"({
+ /* 100ms */
+ "max_out_of_order_duration": 100000000,
+ "node": {
+ "name": "pi1"
+ },
+ "logger_node": {
+ "name": "pi1"
+ },
+ "monotonic_start_time": 1000000,
+ "realtime_start_time": 1000000000000,
+ "log_event_uuid": "30ef1283-81d7-4004-8c36-1c162dbcb2b2",
+ "source_node_boot_uuid": "1d782c63-b3c7-466e-bea9-a01308b43333",
+ "logger_node_boot_uuid": "1d782c63-b3c7-466e-bea9-a01308b43333",
+ "boot_uuids": [
+ "1d782c63-b3c7-466e-bea9-a01308b43333",
+ ],
+ "parts_uuid": "2a05d725-5d5c-4c0b-af42-88de2f3c3876",
+ "parts_index": 0
+})"
+ :
+ R"({
/* 100ms */
"max_out_of_order_duration": 100000000,
"node": {
@@ -593,10 +650,13 @@
std::vector<uint32_t> queue_index_;
};
-using MessageSorterTest = SortingElementTest;
+using MessageSorterTest = SortingElementTest<SortingElementConfig::kMultiNode>;
using MessageSorterDeathTest = MessageSorterTest;
-using PartsMergerTest = SortingElementTest;
-using TimestampMapperTest = SortingElementTest;
+using PartsMergerTest = SortingElementTest<SortingElementConfig::kMultiNode>;
+using TimestampMapperTest =
+ SortingElementTest<SortingElementConfig::kMultiNode>;
+using SingleNodeTimestampMapperTest =
+ SortingElementTest<SortingElementConfig::kSingleNode>;
// Tests that we can pull messages out of a log sorted in order.
TEST_F(MessageSorterTest, Pull) {
@@ -2065,7 +2125,44 @@
}
}
-class BootMergerTest : public SortingElementTest {
+// Validates that we can read timestamps on startup even for single-node logs.
+TEST_F(SingleNodeTimestampMapperTest, QueueTimestampsForSingleNodes) {
+ const aos::monotonic_clock::time_point e = monotonic_clock::epoch();
+ {
+ TestDetachedBufferWriter writer0(logfile0_);
+ writer0.QueueSpan(config0_.span());
+
+ writer0.WriteSizedFlatbuffer(
+ MakeLogMessage(e + chrono::milliseconds(1000), 0, 0x005));
+ writer0.WriteSizedFlatbuffer(
+ MakeLogMessage(e + chrono::milliseconds(2000), 0, 0x006));
+ writer0.WriteSizedFlatbuffer(
+ MakeLogMessage(e + chrono::milliseconds(2000), 0, 0x007));
+ writer0.WriteSizedFlatbuffer(
+ MakeLogMessage(e + chrono::milliseconds(3000), 0, 0x008));
+ }
+
+ const std::vector<LogFile> parts = SortParts({logfile0_});
+ LogFilesContainer log_files(parts);
+
+ ASSERT_EQ(parts[0].logger_node, "pi1");
+
+ size_t mapper0_count = 0;
+ TimestampMapper mapper0("pi1", log_files,
+ TimestampQueueStrategy::kQueueTimestampsAtStartup);
+ mapper0.set_timestamp_callback(
+ [&](TimestampedMessage *) { ++mapper0_count; });
+ mapper0.QueueTimestamps();
+
+ for (int i = 0; i < 4; ++i) {
+ ASSERT_TRUE(mapper0.Front() != nullptr);
+ mapper0.PopFront();
+ }
+ EXPECT_TRUE(mapper0.Front() == nullptr);
+ EXPECT_EQ(mapper0_count, 4u);
+}
+
+class BootMergerTest : public SortingElementTest<> {
public:
BootMergerTest()
: SortingElementTest(),
@@ -2204,7 +2301,7 @@
EXPECT_EQ(output[3].timestamp.time, e + chrono::milliseconds(200));
}
-class RebootTimestampMapperTest : public SortingElementTest {
+class RebootTimestampMapperTest : public SortingElementTest<> {
public:
RebootTimestampMapperTest()
: SortingElementTest(),
@@ -2730,7 +2827,7 @@
}
}
-class SortingDeathTest : public SortingElementTest {
+class SortingDeathTest : public SortingElementTest<> {
public:
SortingDeathTest()
: SortingElementTest(),
@@ -3070,6 +3167,10 @@
aos::realtime_clock::epoch() +
chrono::nanoseconds(time_distribution(random_number_generator_));
+ context.monotonic_remote_transmit_time =
+ aos::monotonic_clock::epoch() +
+ chrono::nanoseconds(time_distribution(random_number_generator_));
+
context.queue_index = uint32_distribution(random_number_generator_);
context.remote_queue_index = uint32_distribution(random_number_generator_);
context.size = data_.size();
@@ -3109,6 +3210,9 @@
builder.add_remote_queue_index(
uint8_distribution(random_number_generator_));
+ builder.add_monotonic_remote_transmit_time(
+ time_distribution(random_number_generator_));
+
fbb.FinishSizePrefixed(builder.Finish());
return fbb.Release();
}
@@ -3254,7 +3358,7 @@
for (const LogType type :
{LogType::kLogMessage, LogType::kLogDeliveryTimeOnly,
- LogType::kLogMessageAndDeliveryTime, LogType::kLogRemoteMessage}) {
+ LogType::kLogRemoteMessage}) {
for (int i = 0; i < 100; ++i) {
aos::Context context = RandomContext();
const uint32_t channel_index =
@@ -3282,11 +3386,18 @@
repacked_message.size(),
PackMessageInline(repacked_message.data(), context, channel_index,
type, 0u, repacked_message.size()));
- EXPECT_EQ(absl::Span<uint8_t>(repacked_message),
+ for (size_t i = 0; i < fbb.GetBufferSpan().size(); ++i) {
+ ASSERT_EQ(absl::Span<uint8_t>(repacked_message)[i],
+ absl::Span<uint8_t>(fbb.GetBufferSpan().data(),
+ fbb.GetBufferSpan().size())[i])
+ << ": On index " << i;
+ }
+ ASSERT_EQ(absl::Span<uint8_t>(repacked_message),
absl::Span<uint8_t>(fbb.GetBufferSpan().data(),
fbb.GetBufferSpan().size()))
<< AnnotateBinaries(schema, "aos/events/logging/logger.bfbs",
- fbb.GetBufferSpan());
+ fbb.GetBufferSpan())
+ << " for log type " << static_cast<int>(type);
// Ok, now we want to confirm that we can build up arbitrary pieces of
// said flatbuffer. Try all of them since it is cheap.
diff --git a/aos/events/logging/logger.fbs b/aos/events/logging/logger.fbs
index 5733fa0..f24922a 100644
--- a/aos/events/logging/logger.fbs
+++ b/aos/events/logging/logger.fbs
@@ -206,6 +206,12 @@
oldest_remote_reliable_monotonic_timestamps:[int64] (id: 28);
oldest_local_reliable_monotonic_timestamps:[int64] (id: 29);
+ // For all channels *excluding* the unreliable channels (ttl != 0), record the
+ // oldest time a reliable message was sent to the kernel to be transmitted over
+ // the network.
+ oldest_remote_reliable_monotonic_transmit_timestamps:[int64] (id: 35);
+ oldest_local_reliable_monotonic_transmit_timestamps:[int64] (id: 36);
+
// For all the remote timestamps which come back to the logger. The "local"
// time here is the logger node boot, and "remote" is the node which sent the
// timestamps.
@@ -257,6 +263,30 @@
// Time this timestamp was received on the monotonic clock of the logger node
// in nanoseconds.
monotonic_timestamp_time:int64 = -9223372036854775808 (id: 8);
+
+ // Time that this message was handed to the kernel to be published over the
+ // network on the remote node.
+ //
+ // monotonic_sent_time captures the time the message was pushed into queue.
+ // For reliable messages (ttl != 0) this may not be equal to the time it was
+ // published.
+ //
+ // For example:
+ // 1. Node A sends a reliable message RM1 to the queue at 10 seconds.
+ // 2. Node B reboots at 80 seconds and requests for all reliable messages.
+ // 3. The reliable message RM1 is sent to node B with a sent time of 10 seconds
+ // even though time is past 80 seconds.
+ // 4. Using this as a valid timestamp point confuses the timestamp solver.
+ //
+ // i.e. monotonic_sent_time for reliable messages is not very useful to
+ // estimate network latency.
+ //
+ // Having more timestamp information such as transmit time can improve reliablity
+ // of the log reader (timestamp solver) especially when reliable messages are involved.
+ //
+ // This data also provides higher visibility into the path of a message across the
+ // network.
+ monotonic_remote_transmit_time:int64 = -9223372036854775808 (id: 9);
}
root_type MessageHeader;
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index 3f0b182..7799c57 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -77,11 +77,10 @@
LOG(INFO) << "Logging data to " << logfile;
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_separate_config(false);
logger.set_polling_period(std::chrono::milliseconds(100));
@@ -141,11 +140,10 @@
LOG(INFO) << "Logging data to " << logfile;
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_separate_config(false);
logger.set_polling_period(std::chrono::milliseconds(100));
@@ -202,11 +200,10 @@
LOG(INFO) << "Logging data to " << logfile1 << " then " << logfile2;
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_polling_period(std::chrono::milliseconds(100));
logger_event_loop->OnRun([base_name1, base_name2, &logger_event_loop,
@@ -277,11 +274,10 @@
LOG(INFO) << "Logging data to " << logfile;
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_separate_config(false);
logger.set_polling_period(std::chrono::milliseconds(100));
@@ -316,11 +312,10 @@
LOG(INFO) << "Logging data to " << logfile1 << " then " << logfile2;
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_separate_config(false);
logger.set_polling_period(std::chrono::milliseconds(100));
@@ -385,11 +380,10 @@
LOG(INFO) << "Logging data to " << logfile0 << " and " << logfile1;
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_separate_config(false);
logger.set_polling_period(std::chrono::milliseconds(100));
diff --git a/aos/events/logging/multinode_logger_test.cc b/aos/events/logging/multinode_logger_test.cc
index 3c438e3..2cf8ffe 100644
--- a/aos/events/logging/multinode_logger_test.cc
+++ b/aos/events/logging/multinode_logger_test.cc
@@ -79,6 +79,44 @@
ForceTimestampBuffering::kAutoBuffer}),
::testing::ValuesIn(SupportedCompressionAlgorithms())));
+// Class to spam Pong messages blindly.
+class PongSender {
+ public:
+ PongSender(EventLoop *loop, std::string_view channel_name)
+ : sender_(loop->MakeSender<examples::Pong>(channel_name)) {
+ loop->AddPhasedLoop(
+ [this](int) {
+ aos::Sender<examples::Pong>::Builder builder = sender_.MakeBuilder();
+ examples::Pong::Builder pong_builder =
+ builder.MakeBuilder<examples::Pong>();
+ CHECK_EQ(builder.Send(pong_builder.Finish()), RawSender::Error::kOk);
+ },
+ chrono::milliseconds(10));
+ }
+
+ private:
+ aos::Sender<examples::Pong> sender_;
+};
+
+// Class to spam Ping messages blindly.
+class PingSender {
+ public:
+ PingSender(EventLoop *loop, std::string_view channel_name)
+ : sender_(loop->MakeSender<examples::Ping>(channel_name)) {
+ loop->AddPhasedLoop(
+ [this](int) {
+ aos::Sender<examples::Ping>::Builder builder = sender_.MakeBuilder();
+ examples::Ping::Builder ping_builder =
+ builder.MakeBuilder<examples::Ping>();
+ CHECK_EQ(builder.Send(ping_builder.Finish()), RawSender::Error::kOk);
+ },
+ chrono::milliseconds(10));
+ }
+
+ private:
+ aos::Sender<examples::Ping> sender_;
+};
+
// Tests that we can write and read simple multi-node log files.
TEST_P(MultinodeLoggerTest, SimpleMultiNode) {
if (file_strategy() == FileStrategy::kCombine) {
@@ -436,6 +474,8 @@
pi1_event_loop->context().monotonic_event_time);
EXPECT_EQ(pi1_event_loop->context().realtime_remote_time,
pi1_event_loop->context().realtime_event_time);
+ EXPECT_EQ(pi1_event_loop->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
++pi1_ping_count;
});
@@ -452,11 +492,21 @@
EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
pi2_ping_count * chrono::milliseconds(10) +
realtime_clock::epoch());
- EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time +
- chrono::microseconds(150),
+ // The message at the start of each second doesn't have wakeup latency
+ // since timing reports and server statistics wake us up already at that
+ // point in time.
+ chrono::nanoseconds offset = chrono::microseconds(150);
+ if (pi2_event_loop->context().monotonic_remote_time.time_since_epoch() %
+ chrono::seconds(1) ==
+ chrono::seconds(0)) {
+ offset = chrono::microseconds(100);
+ }
+ EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time + offset,
pi2_event_loop->context().monotonic_event_time);
- EXPECT_EQ(pi2_event_loop->context().realtime_remote_time +
- chrono::microseconds(150),
+ EXPECT_EQ(pi2_event_loop->context().monotonic_event_time -
+ chrono::microseconds(100),
+ pi2_event_loop->context().monotonic_remote_transmit_time);
+ EXPECT_EQ(pi2_event_loop->context().realtime_remote_time + offset,
pi2_event_loop->context().realtime_event_time);
++pi2_ping_count;
});
@@ -464,63 +514,81 @@
constexpr ssize_t kQueueIndexOffset = -9;
// Confirm that the ping and pong counts both match, and the value also
// matches.
- pi1_event_loop->MakeWatcher(
- "/test", [&pi1_event_loop, &pi1_ping_count,
- &pi1_pong_count](const examples::Pong &pong) {
- VLOG(1) << "Pi1 pong " << FlatbufferToJson(&pong) << " at "
- << pi1_event_loop->context().monotonic_remote_time << " -> "
- << pi1_event_loop->context().monotonic_event_time;
+ pi1_event_loop->MakeWatcher("/test", [&pi1_event_loop, &pi1_ping_count,
+ &pi1_pong_count](
+ const examples::Pong &pong) {
+ VLOG(1) << "Pi1 pong " << FlatbufferToJson(&pong) << " at "
+ << pi1_event_loop->context().monotonic_remote_time << " -> "
+ << pi1_event_loop->context().monotonic_event_time;
- EXPECT_EQ(pi1_event_loop->context().remote_queue_index,
- pi1_pong_count + kQueueIndexOffset);
- EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time,
- chrono::microseconds(200) +
- pi1_pong_count * chrono::milliseconds(10) +
- monotonic_clock::epoch());
- EXPECT_EQ(pi1_event_loop->context().realtime_remote_time,
- chrono::microseconds(200) +
- pi1_pong_count * chrono::milliseconds(10) +
- realtime_clock::epoch());
+ EXPECT_EQ(pi1_event_loop->context().remote_queue_index,
+ pi1_pong_count + kQueueIndexOffset);
- EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time +
- chrono::microseconds(150),
- pi1_event_loop->context().monotonic_event_time);
- EXPECT_EQ(pi1_event_loop->context().realtime_remote_time +
- chrono::microseconds(150),
- pi1_event_loop->context().realtime_event_time);
+ chrono::nanoseconds offset = chrono::microseconds(200);
+ if ((pi1_event_loop->context().monotonic_remote_time.time_since_epoch() -
+ chrono::microseconds(150)) %
+ chrono::seconds(1) ==
+ chrono::seconds(0)) {
+ offset = chrono::microseconds(150);
+ }
- EXPECT_EQ(pong.value(), pi1_pong_count + 1);
- ++pi1_pong_count;
- EXPECT_EQ(pi1_ping_count, pi1_pong_count);
- });
- pi2_event_loop->MakeWatcher(
- "/test", [&pi2_event_loop, &pi2_ping_count,
- &pi2_pong_count](const examples::Pong &pong) {
- VLOG(1) << "Pi2 pong " << FlatbufferToJson(&pong) << " at "
- << pi2_event_loop->context().monotonic_remote_time << " -> "
- << pi2_event_loop->context().monotonic_event_time;
+ EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time,
+ offset + pi1_pong_count * chrono::milliseconds(10) +
+ monotonic_clock::epoch());
+ EXPECT_EQ(pi1_event_loop->context().realtime_remote_time,
+ offset + pi1_pong_count * chrono::milliseconds(10) +
+ realtime_clock::epoch());
- EXPECT_EQ(pi2_event_loop->context().remote_queue_index,
- pi2_pong_count + kQueueIndexOffset);
+ EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time +
+ chrono::microseconds(150),
+ pi1_event_loop->context().monotonic_event_time);
+ EXPECT_EQ(pi1_event_loop->context().realtime_remote_time +
+ chrono::microseconds(150),
+ pi1_event_loop->context().realtime_event_time);
+ EXPECT_EQ(pi1_event_loop->context().monotonic_remote_transmit_time,
+ pi1_event_loop->context().monotonic_event_time -
+ chrono::microseconds(100));
- EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time,
- chrono::microseconds(200) +
- pi2_pong_count * chrono::milliseconds(10) +
- monotonic_clock::epoch());
- EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
- chrono::microseconds(200) +
- pi2_pong_count * chrono::milliseconds(10) +
- realtime_clock::epoch());
+ EXPECT_EQ(pong.value(), pi1_pong_count + 1);
+ ++pi1_pong_count;
+ EXPECT_EQ(pi1_ping_count, pi1_pong_count);
+ });
+ pi2_event_loop->MakeWatcher("/test", [&pi2_event_loop, &pi2_ping_count,
+ &pi2_pong_count](
+ const examples::Pong &pong) {
+ VLOG(1) << "Pi2 pong " << FlatbufferToJson(&pong) << " at "
+ << pi2_event_loop->context().monotonic_remote_time << " -> "
+ << pi2_event_loop->context().monotonic_event_time;
- EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time,
- pi2_event_loop->context().monotonic_event_time);
- EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
- pi2_event_loop->context().realtime_event_time);
+ EXPECT_EQ(pi2_event_loop->context().remote_queue_index,
+ pi2_pong_count + kQueueIndexOffset);
- EXPECT_EQ(pong.value(), pi2_pong_count + 1);
- ++pi2_pong_count;
- EXPECT_EQ(pi2_ping_count, pi2_pong_count);
- });
+ chrono::nanoseconds offset = chrono::microseconds(200);
+ if ((pi2_event_loop->context().monotonic_remote_time.time_since_epoch() -
+ chrono::microseconds(150)) %
+ chrono::seconds(1) ==
+ chrono::seconds(0)) {
+ offset = chrono::microseconds(150);
+ }
+
+ EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time,
+ offset + pi2_pong_count * chrono::milliseconds(10) +
+ monotonic_clock::epoch());
+ EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
+ offset + pi2_pong_count * chrono::milliseconds(10) +
+ realtime_clock::epoch());
+
+ EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time,
+ pi2_event_loop->context().monotonic_event_time);
+ EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
+ pi2_event_loop->context().realtime_event_time);
+ EXPECT_EQ(pi2_event_loop->context().monotonic_remote_transmit_time,
+ monotonic_clock::min_time);
+
+ EXPECT_EQ(pong.value(), pi2_pong_count + 1);
+ ++pi2_pong_count;
+ EXPECT_EQ(pi2_ping_count, pi2_pong_count);
+ });
log_reader_factory.Run();
EXPECT_EQ(pi1_ping_count, 2010);
@@ -1855,6 +1923,9 @@
chrono::nanoseconds(header.realtime_sent_time()));
const aos::monotonic_clock::time_point header_monotonic_remote_time(
chrono::nanoseconds(header.monotonic_remote_time()));
+ const aos::monotonic_clock::time_point
+ header_monotonic_remote_transmit_time(
+ chrono::nanoseconds(header.monotonic_remote_transmit_time()));
const aos::realtime_clock::time_point header_realtime_remote_time(
chrono::nanoseconds(header.realtime_remote_time()));
@@ -1870,11 +1941,28 @@
ASSERT_TRUE(pi1_timestamp_on_pi2_fetcher.FetchNext());
pi1_context = &pi1_timestamp_on_pi1_fetcher.context();
pi2_context = &pi1_timestamp_on_pi2_fetcher.context();
+ // Timestamps don't have wakeup delay, so they show back up after 2
+ // times the network delay on the source node. Confirm that matches
+ // when we are reading the log.
+ EXPECT_EQ(pi1_event_loop->context().monotonic_event_time,
+ pi1_context->monotonic_event_time + 2 * network_delay);
} else if (header.channel_index() == ping_timestamp_channel) {
ASSERT_TRUE(ping_on_pi1_fetcher.FetchNext());
ASSERT_TRUE(ping_on_pi2_fetcher.FetchNext());
pi1_context = &ping_on_pi1_fetcher.context();
pi2_context = &ping_on_pi2_fetcher.context();
+ // Ping messages get picked up faster at the start of each message
+ // when timers wake up. Verify all that behavior matches exactly as
+ // expected when reading the log.
+ EXPECT_EQ(pi1_event_loop->context().monotonic_event_time,
+ pi1_context->monotonic_event_time + 2 * network_delay +
+ ((pi1_event_loop->context().monotonic_event_time -
+ 2 * network_delay)
+ .time_since_epoch() %
+ chrono::nanoseconds(1000000000) ==
+ chrono::nanoseconds(0)
+ ? chrono::nanoseconds(0)
+ : send_delay));
} else {
LOG(FATAL) << "Unknown channel " << FlatbufferToJson(&header) << " "
<< configuration::CleanedChannelToString(
@@ -1899,18 +1987,13 @@
header_realtime_remote_time);
EXPECT_EQ(pi2_context->monotonic_remote_time,
header_monotonic_remote_time);
+ EXPECT_EQ(pi2_context->monotonic_remote_transmit_time,
+ header_monotonic_remote_transmit_time);
EXPECT_EQ(pi1_context->realtime_event_time,
header_realtime_remote_time);
EXPECT_EQ(pi1_context->monotonic_event_time,
header_monotonic_remote_time);
-
- // Time estimation isn't perfect, but we know the clocks were
- // identical when logged, so we know when this should have come back.
- // Confirm we got it when we expected.
- EXPECT_EQ(pi1_event_loop->context().monotonic_event_time,
- pi1_context->monotonic_event_time + 2 * network_delay +
- send_delay);
});
}
for (std::pair<int, std::string> channel :
@@ -1935,6 +2018,9 @@
chrono::nanoseconds(header.realtime_sent_time()));
const aos::monotonic_clock::time_point header_monotonic_remote_time(
chrono::nanoseconds(header.monotonic_remote_time()));
+ const aos::monotonic_clock::time_point
+ header_monotonic_remote_transmit_time(
+ chrono::nanoseconds(header.monotonic_remote_transmit_time()));
const aos::realtime_clock::time_point header_realtime_remote_time(
chrono::nanoseconds(header.realtime_remote_time()));
@@ -1950,11 +2036,20 @@
ASSERT_TRUE(pi2_timestamp_on_pi1_fetcher.FetchNext());
pi2_context = &pi2_timestamp_on_pi2_fetcher.context();
pi1_context = &pi2_timestamp_on_pi1_fetcher.context();
+ // Again, timestamps don't have wakeup delay, so they show back up
+ // after 2 times the network delay on the source node.
+ EXPECT_EQ(pi2_event_loop->context().monotonic_event_time,
+ pi2_context->monotonic_event_time + 2 * network_delay);
} else if (header.channel_index() == pong_timestamp_channel) {
ASSERT_TRUE(pong_on_pi2_fetcher.FetchNext());
ASSERT_TRUE(pong_on_pi1_fetcher.FetchNext());
pi2_context = &pong_on_pi2_fetcher.context();
pi1_context = &pong_on_pi1_fetcher.context();
+ // And Pong messages come back repeatably since they aren't at the
+ // start of a second.
+ EXPECT_EQ(pi2_event_loop->context().monotonic_event_time,
+ pi2_context->monotonic_event_time + 2 * network_delay +
+ send_delay);
} else {
LOG(FATAL) << "Unknown channel " << FlatbufferToJson(&header) << " "
<< configuration::CleanedChannelToString(
@@ -1979,18 +2074,13 @@
header_realtime_remote_time);
EXPECT_EQ(pi1_context->monotonic_remote_time,
header_monotonic_remote_time);
+ EXPECT_EQ(pi1_context->monotonic_remote_transmit_time,
+ header_monotonic_remote_transmit_time);
EXPECT_EQ(pi2_context->realtime_event_time,
header_realtime_remote_time);
EXPECT_EQ(pi2_context->monotonic_event_time,
header_monotonic_remote_time);
-
- // Time estimation isn't perfect, but we know the clocks were
- // identical when logged, so we know when this should have come back.
- // Confirm we got it when we expected.
- EXPECT_EQ(pi2_event_loop->context().monotonic_event_time,
- pi2_context->monotonic_event_time + 2 * network_delay +
- send_delay);
});
}
@@ -2228,7 +2318,7 @@
case 3:
EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
ASSERT_EQ(monotonic_start_time, monotonic_clock::epoch() +
- chrono::nanoseconds(2322999462))
+ chrono::nanoseconds(2323000000))
<< " on " << file;
break;
default:
@@ -2255,7 +2345,7 @@
case 5:
EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
ASSERT_EQ(monotonic_start_time, monotonic_clock::epoch() +
- chrono::nanoseconds(2322999462))
+ chrono::nanoseconds(2323000000))
<< " on " << file;
break;
default:
@@ -2320,6 +2410,18 @@
.oldest_local_unreliable_monotonic_timestamps()
->Get(1)));
const monotonic_clock::time_point
+ oldest_remote_reliable_monotonic_transmit_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_remote_reliable_monotonic_transmit_timestamps()
+ ->Get(1)));
+ const monotonic_clock::time_point
+ oldest_local_reliable_monotonic_transmit_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_local_reliable_monotonic_transmit_timestamps()
+ ->Get(1)));
+ const monotonic_clock::time_point
oldest_remote_reliable_monotonic_timestamps =
monotonic_clock::time_point(chrono::nanoseconds(
log_header->message()
@@ -2362,6 +2464,10 @@
monotonic_clock::max_time);
EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
monotonic_clock::max_time);
+ EXPECT_EQ(oldest_remote_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::max_time);
break;
default:
FAIL();
@@ -2383,6 +2489,10 @@
monotonic_clock::max_time);
EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
monotonic_clock::max_time);
+ EXPECT_EQ(oldest_remote_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90250)));
+ EXPECT_EQ(oldest_local_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90350)));
break;
case 1:
ASSERT_EQ(oldest_remote_monotonic_timestamps,
@@ -2401,7 +2511,13 @@
monotonic_clock::time_point(chrono::microseconds(100000)))
<< file;
EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
- monotonic_clock::time_point(chrono::microseconds(100150)))
+ monotonic_clock::time_point(chrono::microseconds(100100)))
+ << file;
+ EXPECT_EQ(oldest_remote_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90250)))
+ << file;
+ EXPECT_EQ(oldest_local_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90350)))
<< file;
break;
case 2:
@@ -2423,6 +2539,13 @@
EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
monotonic_clock::max_time)
<< file;
+ EXPECT_EQ(oldest_remote_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::milliseconds(1323) +
+ chrono::microseconds(250)))
+ << file;
+ EXPECT_EQ(oldest_local_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(10100350)))
+ << file;
break;
case 3:
ASSERT_EQ(oldest_remote_monotonic_timestamps,
@@ -2441,7 +2564,14 @@
monotonic_clock::time_point(chrono::microseconds(1423000)))
<< file;
EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
- monotonic_clock::time_point(chrono::microseconds(10200150)))
+ monotonic_clock::time_point(chrono::microseconds(10200100)))
+ << file;
+ EXPECT_EQ(oldest_remote_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::milliseconds(1323) +
+ chrono::microseconds(250)))
+ << file;
+ EXPECT_EQ(oldest_local_reliable_monotonic_transmit_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(10100350)))
<< file;
break;
default:
@@ -3124,6 +3254,107 @@
}
}
+// Tests that we can relog with a subset of the original config. This is useful
+// for excluding obsolete or deprecated channels, so they don't appear in the
+// configuration when reading the log.
+TEST_P(MultinodeLoggerTest, LogPartialConfig) {
+ time_converter_.StartEqual();
+ {
+ LoggerState pi1_logger = MakeLogger(pi1_);
+ LoggerState pi2_logger = MakeLogger(pi2_);
+
+ event_loop_factory_.RunFor(chrono::milliseconds(95));
+
+ StartLogger(&pi1_logger);
+ StartLogger(&pi2_logger);
+
+ event_loop_factory_.RunFor(chrono::milliseconds(20000));
+ }
+
+ auto sorted_parts = SortParts(logfiles_);
+ EXPECT_TRUE(AllPartsMatchOutOfOrderDuration(sorted_parts));
+ LogReader reader(sorted_parts);
+ reader.RemapLoggedChannel<aos::examples::Ping>("/test", "/original");
+
+ SimulatedEventLoopFactory log_reader_factory(reader.configuration());
+ log_reader_factory.set_send_delay(chrono::microseconds(0));
+
+ // This sends out the fetched messages and advances time to the start of the
+ // log file.
+ reader.Register(&log_reader_factory);
+
+ const Node *pi1 =
+ configuration::GetNode(log_reader_factory.configuration(), "pi1");
+ const Node *pi2 =
+ configuration::GetNode(log_reader_factory.configuration(), "pi2");
+
+ LOG(INFO) << "Start time " << reader.monotonic_start_time(pi1) << " pi1";
+ LOG(INFO) << "Start time " << reader.monotonic_start_time(pi2) << " pi2";
+ LOG(INFO) << "now pi1 "
+ << log_reader_factory.GetNodeEventLoopFactory(pi1)->monotonic_now();
+ LOG(INFO) << "now pi2 "
+ << log_reader_factory.GetNodeEventLoopFactory(pi2)->monotonic_now();
+
+ EXPECT_THAT(reader.LoggedNodes(),
+ ::testing::ElementsAre(
+ configuration::GetNode(reader.logged_configuration(), pi1),
+ configuration::GetNode(reader.logged_configuration(), pi2)));
+
+ reader.event_loop_factory()->set_send_delay(chrono::microseconds(0));
+
+ const FlatbufferDetachedBuffer<Configuration> partial_configuration_buffer =
+ configuration::GetPartialConfiguration(
+ *reader.event_loop_factory()->configuration(),
+ [](const Channel &channel) {
+ if (channel.name()->string_view().starts_with("/original/")) {
+ LOG(INFO) << "Omitting channel from save_log, channel: "
+ << channel.name()->string_view() << ", "
+ << channel.type()->string_view();
+ return false;
+ }
+ return true;
+ });
+
+ // And confirm we can re-create a log again, while checking the contents.
+ std::vector<std::string> log_files;
+ {
+ const Configuration *partial_configuration =
+ &(partial_configuration_buffer.message());
+
+ LoggerState pi1_logger =
+ MakeLogger(log_reader_factory.GetNodeEventLoopFactory("pi1"),
+ &log_reader_factory, partial_configuration);
+ LoggerState pi2_logger =
+ MakeLogger(log_reader_factory.GetNodeEventLoopFactory("pi2"),
+ &log_reader_factory, partial_configuration);
+
+ pi1_logger.StartLogger(tmp_dir_ + "/logs/relogged1");
+ pi2_logger.StartLogger(tmp_dir_ + "/logs/relogged2");
+
+ log_reader_factory.Run();
+
+ for (auto &x : pi1_logger.log_namer->all_filenames()) {
+ log_files.emplace_back(absl::StrCat(tmp_dir_, "/logs/relogged1_", x));
+ }
+ for (auto &x : pi2_logger.log_namer->all_filenames()) {
+ log_files.emplace_back(absl::StrCat(tmp_dir_, "/logs/relogged2_", x));
+ }
+ }
+
+ reader.Deregister();
+
+ // And verify that we can run the LogReader over the relogged files without
+ // hitting any fatal errors.
+ {
+ auto sorted_parts = SortParts(log_files);
+ EXPECT_TRUE(AllPartsMatchOutOfOrderDuration(sorted_parts));
+ LogReader relogged_reader(sorted_parts);
+ relogged_reader.Register();
+
+ relogged_reader.event_loop_factory()->Run();
+ }
+}
+
// Tests that we properly replay a log where the start time for a node is
// before any data on the node. This can happen if the logger starts before
// data is published. While the scenario below is a bit convoluted, we have
@@ -3288,7 +3519,7 @@
chrono::seconds(1)));
EXPECT_THAT(result[2].second,
::testing::ElementsAre(realtime_clock::epoch() +
- chrono::microseconds(34900150)));
+ chrono::microseconds(34900100)));
}
// Tests that local data before remote data after reboot is properly replayed.
@@ -3440,7 +3671,7 @@
EXPECT_THAT(result[0].first, ::testing::ElementsAre(realtime_clock::epoch()));
EXPECT_THAT(result[0].second,
::testing::ElementsAre(realtime_clock::epoch() +
- chrono::microseconds(11000350)));
+ chrono::microseconds(11000300)));
EXPECT_THAT(result[1].first,
::testing::ElementsAre(
@@ -3448,13 +3679,13 @@
realtime_clock::epoch() + chrono::microseconds(107005000)));
EXPECT_THAT(result[1].second,
::testing::ElementsAre(
- realtime_clock::epoch() + chrono::microseconds(4000150),
- realtime_clock::epoch() + chrono::microseconds(111000200)));
+ realtime_clock::epoch() + chrono::microseconds(4000100),
+ realtime_clock::epoch() + chrono::microseconds(111000150)));
EXPECT_THAT(result[2].first, ::testing::ElementsAre(realtime_clock::epoch()));
EXPECT_THAT(result[2].second,
::testing::ElementsAre(realtime_clock::epoch() +
- chrono::microseconds(11000150)));
+ chrono::microseconds(11000100)));
auto start_stop_result = ConfirmReadable(
filenames, realtime_clock::epoch() + chrono::milliseconds(2000),
@@ -3598,7 +3829,7 @@
EXPECT_THAT(result[0].first, ::testing::ElementsAre(realtime_clock::epoch()));
EXPECT_THAT(result[0].second,
::testing::ElementsAre(realtime_clock::epoch() +
- chrono::microseconds(11000350)));
+ chrono::microseconds(11000300)));
EXPECT_THAT(result[1].first,
::testing::ElementsAre(
@@ -3606,13 +3837,13 @@
realtime_clock::epoch() + chrono::microseconds(6005000)));
EXPECT_THAT(result[1].second,
::testing::ElementsAre(
- realtime_clock::epoch() + chrono::microseconds(4900150),
- realtime_clock::epoch() + chrono::microseconds(11000200)));
+ realtime_clock::epoch() + chrono::microseconds(4900100),
+ realtime_clock::epoch() + chrono::microseconds(11000150)));
EXPECT_THAT(result[2].first, ::testing::ElementsAre(realtime_clock::epoch()));
EXPECT_THAT(result[2].second,
::testing::ElementsAre(realtime_clock::epoch() +
- chrono::microseconds(11000150)));
+ chrono::microseconds(11000100)));
// Confirm we observed the correct start and stop times. We should see the
// reboot here.
@@ -3632,7 +3863,7 @@
realtime_clock::epoch() + chrono::microseconds(6005000)));
EXPECT_THAT(start_stop_result[1].second,
::testing::ElementsAre(
- realtime_clock::epoch() + chrono::microseconds(4900150),
+ realtime_clock::epoch() + chrono::microseconds(4900100),
realtime_clock::epoch() + chrono::seconds(8)));
EXPECT_THAT(
start_stop_result[2].first,
@@ -4585,24 +4816,71 @@
auto result = ConfirmReadable(filenames);
}
-// Class to spam Pong messages blindly.
-class PongSender {
- public:
- PongSender(EventLoop *loop, std::string_view channel_name)
- : sender_(loop->MakeSender<examples::Pong>(channel_name)) {
- loop->AddPhasedLoop(
- [this](int) {
- aos::Sender<examples::Pong>::Builder builder = sender_.MakeBuilder();
- examples::Pong::Builder pong_builder =
- builder.MakeBuilder<examples::Pong>();
- CHECK_EQ(builder.Send(pong_builder.Finish()), RawSender::Error::kOk);
- },
- chrono::milliseconds(10));
+// Tests that only having a delayed, reliable message from a boot results in a
+// readable log.
+//
+// Note: this is disabled since it doesn't work yet. Un-disable this when the
+// code is fixed!
+TEST(MultinodeLoggerLoopTest, ReliableOnlyTimestamps) {
+ util::UnlinkRecursive(aos::testing::TestTmpDir() + "/logs");
+ std::filesystem::create_directory(aos::testing::TestTmpDir() + "/logs");
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(
+ ArtifactPath("aos/events/logging/"
+ "multinode_pingpong_reboot_reliable_only_config.json"));
+ message_bridge::TestingTimeConverter time_converter(
+ configuration::NodesCount(&config.message()));
+ SimulatedEventLoopFactory event_loop_factory(&config.message());
+ event_loop_factory.SetTimeConverter(&time_converter);
+
+ constexpr chrono::nanoseconds kRebootTime = chrono::seconds(100);
+ {
+ time_converter.AddNextTimestamp(
+ distributed_clock::epoch(),
+ {BootTimestamp::epoch(), BootTimestamp::epoch()});
+ time_converter.AddNextTimestamp(
+ distributed_clock::epoch() + kRebootTime,
+ {BootTimestamp::epoch() + kRebootTime,
+ BootTimestamp{.boot = 1, .time = monotonic_clock::epoch()}});
}
- private:
- aos::Sender<examples::Pong> sender_;
-};
+ const std::string kLogfile1_1 =
+ aos::testing::TestTmpDir() + "/logs/multi_logfile1/";
+
+ NodeEventLoopFactory *const pi1 =
+ event_loop_factory.GetNodeEventLoopFactory("pi1");
+
+ // We want unreliable timestamps from one boot, a reliable timestamp from the
+ // same boot, and then a long delayed reliable timestamp from the second boot.
+ // This produces conflicting information about when the second boot happened.
+ std::vector<std::string> filenames;
+ PingSender *app1 = pi1->AlwaysStart<PingSender>("pingsender", "/atest1");
+ PingSender *app2 = pi1->AlwaysStart<PingSender>("pingsender", "/atest2");
+ event_loop_factory.RunFor(chrono::seconds(1));
+ pi1->Stop(app2);
+ event_loop_factory.RunFor(kRebootTime - chrono::seconds(2));
+ pi1->Stop(app1);
+
+ event_loop_factory.RunFor(chrono::seconds(1) + kRebootTime * 2);
+
+ {
+ // Collect a small log after reboot.
+ LoggerState pi1_logger = MakeLoggerState(
+ pi1, &event_loop_factory, SupportedCompressionAlgorithms()[0],
+ FileStrategy::kKeepSeparate);
+ pi1_logger.StartLogger(kLogfile1_1);
+
+ event_loop_factory.RunFor(chrono::seconds(1));
+
+ pi1_logger.AppendAllFilenames(&filenames);
+ }
+
+ // Make sure we can read this.
+ const std::vector<LogFile> sorted_parts = SortParts(filenames);
+ EXPECT_TRUE(AllPartsMatchOutOfOrderDuration(sorted_parts));
+ auto result = ConfirmReadable(filenames);
+}
// Tests that we log correctly as nodes connect slowly.
TEST(MultinodeLoggerLoopTest, StaggeredConnect) {
diff --git a/aos/events/logging/multinode_logger_test_lib.cc b/aos/events/logging/multinode_logger_test_lib.cc
index ed62a2e..3bf6207 100644
--- a/aos/events/logging/multinode_logger_test_lib.cc
+++ b/aos/events/logging/multinode_logger_test_lib.cc
@@ -29,7 +29,8 @@
configuration::GetNode(configuration, node->node()),
nullptr,
params,
- file_strategy};
+ file_strategy,
+ nullptr};
}
std::unique_ptr<MultiNodeFilesLogNamer> LoggerState::MakeLogNamer(
@@ -56,12 +57,17 @@
absl::StrCat("logger_sha1_", event_loop->node()->name()->str()));
logger->set_logger_version(
absl::StrCat("logger_version_", event_loop->node()->name()->str()));
- event_loop->OnRun([this, logfile_base]() {
+ CHECK(start_timer == nullptr)
+ << ": Test fixture doesn't yet supporting starting a logger twice.";
+
+ // Use a timer for starting since OnRun can only happen at the actual startup.
+ start_timer = event_loop->AddTimer([this, logfile_base]() {
std::unique_ptr<MultiNodeFilesLogNamer> namer = MakeLogNamer(logfile_base);
log_namer = namer.get();
logger->StartLogging(std::move(namer));
});
+ start_timer->Schedule(event_loop->monotonic_now());
}
void LoggerState::AppendAllFilenames(std::vector<std::string> *filenames) {
diff --git a/aos/events/logging/multinode_logger_test_lib.h b/aos/events/logging/multinode_logger_test_lib.h
index 8f64f66..a8e5969 100644
--- a/aos/events/logging/multinode_logger_test_lib.h
+++ b/aos/events/logging/multinode_logger_test_lib.h
@@ -69,6 +69,7 @@
MultiNodeFilesLogNamer *log_namer;
CompressionParams params;
FileStrategy file_strategy;
+ aos::TimerHandler *start_timer;
void AppendAllFilenames(std::vector<std::string> *filenames);
@@ -76,13 +77,13 @@
};
constexpr std::string_view kCombinedConfigSha1() {
- return "71eb8341221fbabefb4ddde43bcebf794fd5855e3ad77786a1db0f9e27a39091";
+ return "adf6a65be9b9a00d85ad1db4c78495e46d9c35b883ef95581c46222b2624d79d";
}
constexpr std::string_view kSplitConfigSha1() {
- return "f61d45dc0bda026e852e2da9b3e5c2c7f1c89c9f7958cfba3d02e2c960416f04";
+ return "7998834e993bcf000c8f03f6fcc5cc63650fdbd1f42ff0a2d2bdbbf1182e3104";
}
constexpr std::string_view kReloggedSplitConfigSha1() {
- return "3d8fd3d13955b517ee3d66a50b5e4dd7a13fd648f469d16910990418bcfc6beb";
+ return "5e800fdbaf6a088f33d5df42a58d803a33caa33eea269fef9e390b0306e9c11e";
}
LoggerState MakeLoggerState(NodeEventLoopFactory *node,
diff --git a/aos/events/logging/multinode_pingpong_reboot_reliable_only.json b/aos/events/logging/multinode_pingpong_reboot_reliable_only.json
new file mode 100644
index 0000000..cfe421d
--- /dev/null
+++ b/aos/events/logging/multinode_pingpong_reboot_reliable_only.json
@@ -0,0 +1,214 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ /* Logged on pi1 locally */
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "logger": "LOCAL_LOGGER",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "logger": "LOCAL_LOGGER",
+ "source_node": "pi2"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.DynamicLogCommand",
+ "logger": "LOCAL_LOGGER",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.DynamicLogCommand",
+ "logger": "LOCAL_LOGGER",
+ "source_node": "pi2"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "logger": "LOCAL_LOGGER",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "logger": "LOCAL_LOGGER",
+ "source_node": "pi2"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "timestamp_logger": "NOT_LOGGED"
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "timestamp_logger": "NOT_LOGGED"
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "num_senders": 2,
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "num_senders": 2,
+ "source_node": "pi2"
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/atest1/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "num_senders": 2,
+ "source_node": "pi1",
+ "frequency": 150
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/atest2/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "num_senders": 2,
+ "source_node": "pi1",
+ "frequency": 150
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/atest3/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "num_senders": 2,
+ "source_node": "pi1",
+ "frequency": 150
+ },
+ {
+ "name": "/atest1",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "logger": "LOCAL_AND_REMOTE_LOGGER",
+ "logger_nodes": ["pi2"],
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ],
+ "frequency": 150
+ },
+ {
+ "name": "/atest2",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "logger": "LOCAL_AND_REMOTE_LOGGER",
+ "logger_nodes": ["pi2"],
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "frequency": 150
+ },
+ {
+ "name": "/atest3",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "frequency": 150
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1",
+ "hostname": "raspberrypi",
+ "port": 9971
+ },
+ {
+ "name": "pi2",
+ "hostname": "raspberrypi2",
+ "port": 9971
+ }
+ ]
+}
diff --git a/aos/events/logging/realtime_replay_test.cc b/aos/events/logging/realtime_replay_test.cc
index 4118550..dd4aaf0 100644
--- a/aos/events/logging/realtime_replay_test.cc
+++ b/aos/events/logging/realtime_replay_test.cc
@@ -88,11 +88,10 @@
TEST_F(RealtimeLoggerTest, RealtimeReplay) {
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(std::chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_separate_config(false);
logger.set_polling_period(std::chrono::milliseconds(100));
@@ -123,11 +122,10 @@
// is included on a single node config
TEST_F(RealtimeLoggerTest, SingleNodeReplayChannels) {
{
- std::unique_ptr<EventLoop> logger_event_loop =
- event_loop_factory_.MakeEventLoop("logger");
-
event_loop_factory_.RunFor(std::chrono::milliseconds(95));
+ std::unique_ptr<EventLoop> logger_event_loop =
+ event_loop_factory_.MakeEventLoop("logger");
Logger logger(logger_event_loop.get());
logger.set_separate_config(false);
logger.set_polling_period(std::chrono::milliseconds(100));
diff --git a/aos/events/shm_event_loop.cc b/aos/events/shm_event_loop.cc
index 7f06ee0..9d010b7 100644
--- a/aos/events/shm_event_loop.cc
+++ b/aos/events/shm_event_loop.cc
@@ -237,6 +237,7 @@
ipc_lib::LocklessQueueReader::Result read_result = reader_.Read(
queue_index.index(), &context_.monotonic_event_time,
&context_.realtime_event_time, &context_.monotonic_remote_time,
+ &context_.monotonic_remote_transmit_time,
&context_.realtime_remote_time, &context_.remote_queue_index,
&context_.source_boot_uuid, &context_.size, copy_buffer, std::move(fn));
@@ -457,6 +458,7 @@
Error DoSend(size_t length,
aos::monotonic_clock::time_point monotonic_remote_time,
aos::realtime_clock::time_point realtime_remote_time,
+ aos::monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index,
const UUID &source_boot_uuid) override {
shm_event_loop()->CheckCurrentThread();
@@ -464,9 +466,9 @@
<< ": Sent too big a message on "
<< configuration::CleanedChannelToString(channel());
const auto result = lockless_queue_sender_.Send(
- length, monotonic_remote_time, realtime_remote_time, remote_queue_index,
- source_boot_uuid, &monotonic_sent_time_, &realtime_sent_time_,
- &sent_queue_index_);
+ length, monotonic_remote_time, realtime_remote_time,
+ monotonic_remote_transmit_time, remote_queue_index, source_boot_uuid,
+ &monotonic_sent_time_, &realtime_sent_time_, &sent_queue_index_);
CHECK_NE(result, ipc_lib::LocklessQueueSender::Result::INVALID_REDZONE)
<< ": Somebody wrote outside the buffer of their message on channel "
<< configuration::CleanedChannelToString(channel());
@@ -480,6 +482,7 @@
Error DoSend(const void *msg, size_t length,
aos::monotonic_clock::time_point monotonic_remote_time,
aos::realtime_clock::time_point realtime_remote_time,
+ aos::monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index,
const UUID &source_boot_uuid) override {
shm_event_loop()->CheckCurrentThread();
@@ -488,8 +491,9 @@
<< configuration::CleanedChannelToString(channel());
const auto result = lockless_queue_sender_.Send(
reinterpret_cast<const char *>(msg), length, monotonic_remote_time,
- realtime_remote_time, remote_queue_index, source_boot_uuid,
- &monotonic_sent_time_, &realtime_sent_time_, &sent_queue_index_);
+ realtime_remote_time, monotonic_remote_transmit_time,
+ remote_queue_index, source_boot_uuid, &monotonic_sent_time_,
+ &realtime_sent_time_, &sent_queue_index_);
CHECK_NE(result, ipc_lib::LocklessQueueSender::Result::INVALID_REDZONE)
<< ": Somebody wrote outside the buffer of their message on "
diff --git a/aos/events/shm_event_loop_test.cc b/aos/events/shm_event_loop_test.cc
index 4db0c7d..bae834a 100644
--- a/aos/events/shm_event_loop_test.cc
+++ b/aos/events/shm_event_loop_test.cc
@@ -25,12 +25,12 @@
}
// Clean up anything left there before.
- unlink((FLAGS_shm_base + "/test/aos.TestMessage.v6").c_str());
- unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v6").c_str());
- unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v6").c_str());
- unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v6").c_str());
- unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v6").c_str());
- unlink((FLAGS_shm_base + "/aos/aos.logging.LogMessageFbs.v6").c_str());
+ unlink((FLAGS_shm_base + "/test/aos.TestMessage.v7").c_str());
+ unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v7").c_str());
+ unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v7").c_str());
+ unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v7").c_str());
+ unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v7").c_str());
+ unlink((FLAGS_shm_base + "/aos/aos.logging.LogMessageFbs.v7").c_str());
}
~ShmEventLoopTestFactory() { FLAGS_override_hostname = ""; }
diff --git a/aos/events/simulated_event_loop.cc b/aos/events/simulated_event_loop.cc
index 5cb67b4..1b05841 100644
--- a/aos/events/simulated_event_loop.cc
+++ b/aos/events/simulated_event_loop.cc
@@ -356,18 +356,21 @@
Error DoSend(size_t length, monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index,
const UUID &source_boot_uuid) override;
Error DoSend(const void *msg, size_t size,
monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index,
const UUID &source_boot_uuid) override;
Error DoSend(const SharedSpan data,
aos::monotonic_clock::time_point monotonic_remote_time,
aos::realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index,
const UUID &source_boot_uuid) override;
@@ -408,7 +411,8 @@
CHECK(!fell_behind_) << ": Got behind on "
<< configuration::StrippedChannelToString(
- simulated_channel_->channel());
+ simulated_channel_->channel())
+ << " on " << NodeName(event_loop()->node());
if (fn) {
Context context = msgs_.front()->context;
@@ -857,8 +861,13 @@
->emplace(SimpleChannel(channel),
std::unique_ptr<SimulatedChannel>(new SimulatedChannel(
channel,
+ // There are a lot of tests which assume that 100 hz
+ // messages can actually be sent out at 100 hz and
+ // forwarded. The jitter in wakeups causes small
+ // variation in timing. Ignore that.
configuration::ChannelStorageDuration(
- configuration(), channel),
+ configuration(), channel) -
+ send_delay(),
scheduler_)))
.first;
}
@@ -1038,7 +1047,7 @@
// Remove times that are greater than or equal to a channel_storage_duration_
// ago
while (!last_times_.empty() &&
- (now - last_times_.front() >= channel_storage_duration_)) {
+ (now >= channel_storage_duration_ + last_times_.front())) {
last_times_.pop();
}
@@ -1101,6 +1110,7 @@
RawSender::Error SimulatedSender::DoSend(
size_t length, monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid) {
// The allocations in here are due to infrastructure and don't count in the
// no mallocs in RT code.
@@ -1120,6 +1130,8 @@
message_->context.realtime_event_time = simulated_event_loop_->realtime_now();
message_->context.realtime_remote_time = realtime_remote_time;
message_->context.source_boot_uuid = source_boot_uuid;
+ message_->context.monotonic_remote_transmit_time =
+ monotonic_remote_transmit_time;
CHECK_LE(length, message_->context.size);
message_->context.size = length;
@@ -1131,19 +1143,14 @@
VLOG(1) << simulated_event_loop_->distributed_now() << " "
<< NodeName(simulated_event_loop_->node())
<< simulated_event_loop_->monotonic_now() << " "
- << simulated_event_loop_->name()
- << "\nMessages were sent too fast:\n"
- << "For channel: "
- << configuration::CleanedChannelToString(
- simulated_channel_->channel())
- << '\n'
- << "Tried to send more than " << simulated_channel_->queue_size()
+ << simulated_event_loop_->name() << " -> SentTooFast "
+ << configuration::StrippedChannelToString(channel())
+ << ", Tried to send more than " << simulated_channel_->queue_size()
<< " (queue size) messages in the last "
<< std::chrono::duration<double>(
simulated_channel_->channel_storage_duration())
.count()
- << " seconds (channel storage duration)"
- << "\n\n";
+ << " seconds (channel storage duration)";
return Error::kMessagesSentTooFast;
}
@@ -1162,6 +1169,7 @@
const void *msg, size_t size,
monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid) {
CHECK_LE(size, this->size())
<< ": Attempting to send too big a message on "
@@ -1176,12 +1184,14 @@
memcpy(mutable_span.data(), msg, size);
return DoSend(size, monotonic_remote_time, realtime_remote_time,
- remote_queue_index, source_boot_uuid);
+ monotonic_remote_transmit_time, remote_queue_index,
+ source_boot_uuid);
}
RawSender::Error SimulatedSender::DoSend(
const SharedSpan data, monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid) {
CHECK_LE(data->size(), this->size())
<< ": Attempting to send too big a message on "
@@ -1192,7 +1202,8 @@
message_ = SimulatedMessage::Make(simulated_channel_, data);
return DoSend(data->size(), monotonic_remote_time, realtime_remote_time,
- remote_queue_index, source_boot_uuid);
+ monotonic_remote_transmit_time, remote_queue_index,
+ source_boot_uuid);
}
SimulatedTimerHandler::SimulatedTimerHandler(
@@ -1604,6 +1615,8 @@
result->SkipTimingReport();
}
+ // TODO(austin): You shouldn't be able to make an event loop before t=0...
+
VLOG(1) << scheduler_.distributed_now() << " " << NodeName(node())
<< monotonic_now() << " MakeEventLoop(\"" << result->name() << "\")";
return result;
diff --git a/aos/events/simulated_event_loop.h b/aos/events/simulated_event_loop.h
index 6d88aa6..e976544 100644
--- a/aos/events/simulated_event_loop.h
+++ b/aos/events/simulated_event_loop.h
@@ -245,6 +245,10 @@
template <class Main, class... Args>
Main *AlwaysStart(std::string_view name, Args &&...args);
+ // Stops an application given the pointer to the application started.
+ template <class Main>
+ void Stop(Main *application);
+
// Returns the simulated network delay for messages forwarded between nodes.
std::chrono::nanoseconds network_delay() const {
return factory_->network_delay();
@@ -341,6 +345,8 @@
: event_loop(node_factory->MakeEventLoop(name)) {}
virtual ~Application() {}
+ virtual void *application() = 0;
+
std::unique_ptr<EventLoop> event_loop;
};
@@ -365,6 +371,8 @@
}
~TypedApplication() override {}
+ void *application() override { return &main; }
+
Main main;
};
@@ -392,6 +400,16 @@
return main_ptr;
}
+template <class Main>
+void NodeEventLoopFactory::Stop(Main *application) {
+ auto it = std::remove_if(
+ applications_.begin(), applications_.end(),
+ [application](const std::unique_ptr<Application> &app) {
+ return app->application() == static_cast<void *>(application);
+ });
+ applications_.erase(it, applications_.end());
+}
+
inline monotonic_clock::time_point NodeEventLoopFactory::monotonic_now() const {
// TODO(austin): Confirm that time never goes backwards?
return scheduler_.monotonic_now();
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index 050c9a3..745a273 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -7,6 +7,7 @@
#include "gtest/gtest.h"
#include "aos/events/event_loop_param_test.h"
+#include "aos/events/function_scheduler.h"
#include "aos/events/logging/logger_generated.h"
#include "aos/events/message_counter.h"
#include "aos/events/ping_lib.h"
@@ -715,7 +716,7 @@
EXPECT_EQ(connection->partial_deliveries(), 0);
EXPECT_TRUE(connection->has_monotonic_offset());
- EXPECT_EQ(connection->monotonic_offset(), 150000);
+ EXPECT_EQ(connection->monotonic_offset(), 100000);
EXPECT_EQ(connection->connection_count(), 1u);
EXPECT_EQ(connection->connected_since_time(), 0);
}
@@ -735,7 +736,7 @@
EXPECT_GT(connection->received_packets(), 50);
EXPECT_EQ(connection->partial_deliveries(), 0);
EXPECT_TRUE(connection->has_monotonic_offset());
- EXPECT_EQ(connection->monotonic_offset(), 150000);
+ EXPECT_EQ(connection->monotonic_offset(), 100000);
EXPECT_EQ(connection->connection_count(), 1u);
EXPECT_EQ(connection->connected_since_time(), 0);
++pi2_client_statistics_count;
@@ -754,7 +755,7 @@
EXPECT_GE(connection->received_packets(), 5);
EXPECT_EQ(connection->partial_deliveries(), 0);
EXPECT_TRUE(connection->has_monotonic_offset());
- EXPECT_EQ(connection->monotonic_offset(), 150000);
+ EXPECT_EQ(connection->monotonic_offset(), 100000);
EXPECT_EQ(connection->connection_count(), 1u);
EXPECT_EQ(connection->connected_since_time(), 0);
++pi3_client_statistics_count;
@@ -798,8 +799,10 @@
[pi1_timestamp_channel, ping_timestamp_channel, &ping_on_pi2_fetcher,
&ping_on_pi1_fetcher, &pi1_on_pi2_timestamp_fetcher,
&pi1_on_pi1_timestamp_fetcher, &simulated_event_loop_factory, pi2,
- channel_index = channel.first](const RemoteMessage &header) {
- VLOG(1) << aos::FlatbufferToJson(&header);
+ channel_index = channel.first,
+ channel_name = channel.second](const RemoteMessage &header) {
+ VLOG(1) << channel_name << " aos::message_bridge::RemoteMessage -> "
+ << aos::FlatbufferToJson(&header);
EXPECT_TRUE(header.has_boot_uuid());
EXPECT_EQ(UUID::FromVector(header.boot_uuid()),
simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
@@ -811,6 +814,9 @@
chrono::nanoseconds(header.realtime_sent_time()));
const aos::monotonic_clock::time_point header_monotonic_remote_time(
chrono::nanoseconds(header.monotonic_remote_time()));
+ const aos::monotonic_clock::time_point
+ header_monotonic_remote_transmit_time(
+ chrono::nanoseconds(header.monotonic_remote_transmit_time()));
const aos::realtime_clock::time_point header_realtime_remote_time(
chrono::nanoseconds(header.realtime_remote_time()));
@@ -836,6 +842,9 @@
pi1_context = &pi1_on_pi1_timestamp_fetcher.context();
pi2_context = &pi1_on_pi2_timestamp_fetcher.context();
+
+ EXPECT_EQ(header_monotonic_remote_transmit_time,
+ pi2_context->monotonic_remote_time);
} else if (header.channel_index() == ping_timestamp_channel) {
// Find the forwarded message.
while (ping_on_pi2_fetcher.context().monotonic_event_time <
@@ -851,6 +860,10 @@
pi1_context = &ping_on_pi1_fetcher.context();
pi2_context = &ping_on_pi2_fetcher.context();
+
+ EXPECT_EQ(header_monotonic_remote_transmit_time,
+ pi2_context->monotonic_event_time -
+ simulated_event_loop_factory.network_delay());
} else {
LOG(FATAL) << "Unknown channel";
}
@@ -868,6 +881,8 @@
header_realtime_remote_time);
EXPECT_EQ(pi2_context->monotonic_remote_time,
header_monotonic_remote_time);
+ EXPECT_EQ(pi2_context->monotonic_remote_transmit_time,
+ header_monotonic_remote_transmit_time);
// Confirm the forwarded message also matches the source message.
EXPECT_EQ(pi1_context->queue_index, header.remote_queue_index());
@@ -2195,7 +2210,15 @@
::std::unique_ptr<EventLoop> ping_event_loop = pi2->MakeEventLoop("pong");
aos::Fetcher<examples::Ping> fetcher =
ping_event_loop->MakeFetcher<examples::Ping>("/reliable");
- EXPECT_TRUE(fetcher.Fetch());
+ ASSERT_TRUE(fetcher.Fetch());
+ EXPECT_EQ(fetcher.context().monotonic_remote_time,
+ monotonic_clock::epoch());
+ // Message bridge picks up the Ping message immediately on reboot.
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::epoch());
+ EXPECT_EQ(fetcher.context().monotonic_event_time,
+ monotonic_clock::epoch() + factory.network_delay());
+ ASSERT_FALSE(fetcher.Fetch());
}
factory.RunFor(chrono::seconds(1));
@@ -2204,7 +2227,15 @@
::std::unique_ptr<EventLoop> ping_event_loop = pi2->MakeEventLoop("pong");
aos::Fetcher<examples::Ping> fetcher =
ping_event_loop->MakeFetcher<examples::Ping>("/reliable");
- EXPECT_TRUE(fetcher.Fetch());
+ ASSERT_TRUE(fetcher.Fetch());
+ EXPECT_EQ(fetcher.context().monotonic_remote_time,
+ monotonic_clock::epoch());
+ // Message bridge picks up the Ping message immediately on reboot.
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::epoch() + chrono::seconds(1));
+ EXPECT_EQ(fetcher.context().monotonic_event_time,
+ monotonic_clock::epoch() + factory.network_delay());
+ ASSERT_FALSE(fetcher.Fetch());
}
EXPECT_NE(pi2_boot_uuid, pi2->boot_uuid());
}
@@ -2260,6 +2291,8 @@
monotonic_clock::epoch() + factory.network_delay());
EXPECT_EQ(fetcher.context().monotonic_remote_time,
monotonic_clock::epoch());
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::epoch() + chrono::seconds(1));
}
{
::std::unique_ptr<EventLoop> pi1_event_loop = pi1->MakeEventLoop("pong");
@@ -2271,6 +2304,8 @@
factory.network_delay());
EXPECT_EQ(fetcher.context().monotonic_remote_time,
monotonic_clock::epoch() - std::chrono::seconds(1));
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::epoch());
}
}
@@ -2461,4 +2496,525 @@
}
}
+// Struct to capture the expected time a message should be received (and it's
+// value). This is from the perspective of the node receiving the message.
+struct ExpectedTimestamps {
+ // The time that the message was published on the sending node's monotonic
+ // clock.
+ monotonic_clock::time_point remote_time;
+ // The time that the message was virtually transmitted over the virtual
+ // network on the sending node's monotonic clock.
+ monotonic_clock::time_point remote_transmit_time;
+ // The time that the message was received on the receiving node's clock.
+ monotonic_clock::time_point event_time;
+ // The value inside the message.
+ int value;
+};
+
+// Tests that rapidly sent messages get timestamped correctly.
+TEST(SimulatedEventLoopTest, TransmitTimestamps) {
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(
+ ArtifactPath("aos/events/multinode_pingpong_test_split_config.json"));
+
+ message_bridge::TestingTimeConverter time(
+ configuration::NodesCount(&config.message()));
+ SimulatedEventLoopFactory factory(&config.message());
+ factory.SetTimeConverter(&time);
+ time.StartEqual();
+
+ NodeEventLoopFactory *pi1 = factory.GetNodeEventLoopFactory("pi1");
+ NodeEventLoopFactory *pi2 = factory.GetNodeEventLoopFactory("pi2");
+
+ ::std::unique_ptr<EventLoop> ping_event_loop = pi2->MakeEventLoop("pong");
+ aos::Fetcher<examples::Ping> fetcher =
+ ping_event_loop->MakeFetcher<examples::Ping>("/reliable");
+ EXPECT_FALSE(fetcher.Fetch());
+
+ {
+ ::std::unique_ptr<EventLoop> ping_event_loop = pi1->MakeEventLoop("ping");
+ FunctionScheduler run_at(ping_event_loop.get());
+ aos::Sender<examples::Ping> test_message_sender =
+ ping_event_loop->MakeSender<examples::Ping>("/reliable");
+ aos::monotonic_clock::time_point now = ping_event_loop->monotonic_now();
+ for (const std::chrono::nanoseconds dt :
+ {chrono::microseconds(5000), chrono::microseconds(1),
+ chrono::microseconds(2), chrono::microseconds(70),
+ chrono::microseconds(63), chrono::microseconds(140)}) {
+ now += dt;
+ run_at.ScheduleAt([&]() { SendPing(&test_message_sender, 1); }, now);
+ }
+
+ now += chrono::milliseconds(10);
+
+ factory.RunFor(now - ping_event_loop->monotonic_now());
+ }
+
+ const monotonic_clock::time_point e = monotonic_clock::epoch();
+ const chrono::nanoseconds send_delay = factory.send_delay();
+ const chrono::nanoseconds network_delay = factory.network_delay();
+
+ const std::vector<ExpectedTimestamps> expected_values = {
+ // First message shows up after wakeup + network delay as expected.
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(5000),
+ .remote_transmit_time = e + chrono::microseconds(5000) + send_delay,
+ .event_time =
+ e + chrono::microseconds(5000) + send_delay + network_delay,
+ .value = 1,
+ },
+ // Next message is close enough that it gets picked up at the same wakeup.
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(5001),
+ .remote_transmit_time = e + chrono::microseconds(5000) + send_delay,
+ .event_time =
+ e + chrono::microseconds(5000) + send_delay + network_delay,
+ .value = 1,
+ },
+ // Same for the third.
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(5003),
+ .remote_transmit_time = e + chrono::microseconds(5000) + send_delay,
+ .event_time =
+ e + chrono::microseconds(5000) + send_delay + network_delay,
+ .value = 1,
+ },
+ // Fourth waits long enough to do the right thing.
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(5073),
+ .remote_transmit_time = e + chrono::microseconds(5073) + send_delay,
+ .event_time =
+ e + chrono::microseconds(5073) + send_delay + network_delay,
+ .value = 1,
+ },
+ // Fifth waits long enough to do the right thing as well (but kicks off
+ // while the fourth is in flight over the network).
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(5136),
+ .remote_transmit_time = e + chrono::microseconds(5136) + send_delay,
+ .event_time =
+ e + chrono::microseconds(5136) + send_delay + network_delay,
+ .value = 1,
+ },
+ // Sixth waits long enough to do the right thing as well (but kicks off
+ // while the fifth is in flight over the network and has almost landed).
+ // The timer wakeup for the Timestamp message coming back will find the
+ // sixth message a little bit early.
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(5276),
+ .remote_transmit_time = e + chrono::microseconds(5273) + send_delay,
+ .event_time =
+ e + chrono::microseconds(5273) + send_delay + network_delay,
+ .value = 1,
+ },
+ };
+
+ for (const ExpectedTimestamps value : expected_values) {
+ ASSERT_TRUE(fetcher.FetchNext());
+ EXPECT_EQ(fetcher.context().monotonic_remote_time, value.remote_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ value.remote_transmit_time);
+ EXPECT_EQ(fetcher.context().monotonic_event_time, value.event_time);
+ EXPECT_EQ(fetcher->value(), value.value);
+ }
+
+ ASSERT_FALSE(fetcher.FetchNext());
+}
+
+// Tests that a reliable message gets forwarded if it was sent originally when
+// nodes were disconnected.
+TEST_F(SimulatedEventLoopDisconnectTest, ReliableMessageSendsOnConnect) {
+ time.StartEqual();
+ factory.SkipTimingReport();
+ factory.DisableStatistics();
+
+ NodeEventLoopFactory *pi1 = factory.GetNodeEventLoopFactory("pi1");
+ NodeEventLoopFactory *pi2 = factory.GetNodeEventLoopFactory("pi2");
+
+ // Fully disconnect the nodes.
+ pi1->Disconnect(pi2->node());
+ pi2->Disconnect(pi1->node());
+
+ std::unique_ptr<aos::EventLoop> pi2_event_loop =
+ pi2->MakeEventLoop("fetcher");
+ aos::Fetcher<examples::Ping> pi2_reliable_fetcher =
+ pi2_event_loop->MakeFetcher<examples::Ping>("/reliable");
+
+ factory.RunFor(chrono::milliseconds(100));
+
+ {
+ std::unique_ptr<aos::EventLoop> pi1_event_loop =
+ pi1->MakeEventLoop("sender");
+ aos::Sender<examples::Ping> pi1_reliable_sender =
+ pi1_event_loop->MakeSender<examples::Ping>("/reliable");
+ FunctionScheduler run_at(pi1_event_loop.get());
+ aos::monotonic_clock::time_point now = pi1_event_loop->monotonic_now();
+ for (int i = 0; i < 100; ++i) {
+ run_at.ScheduleAt([&, i = i]() { SendPing(&pi1_reliable_sender, i); },
+ now);
+ now += chrono::milliseconds(100);
+ }
+ now += chrono::milliseconds(50);
+
+ factory.RunFor(now - pi1_event_loop->monotonic_now());
+ }
+
+ ASSERT_FALSE(pi2_reliable_fetcher.Fetch());
+
+ pi1->Connect(pi2->node());
+ pi2->Connect(pi1->node());
+
+ factory.RunFor(chrono::milliseconds(1));
+
+ ASSERT_TRUE(pi2_reliable_fetcher.Fetch());
+ ASSERT_EQ(pi2_reliable_fetcher.context().monotonic_remote_time,
+ monotonic_clock::epoch() + chrono::milliseconds(10000));
+ ASSERT_EQ(pi2_reliable_fetcher.context().monotonic_remote_transmit_time,
+ monotonic_clock::epoch() + chrono::milliseconds(10150));
+ ASSERT_EQ(pi2_reliable_fetcher.context().monotonic_event_time,
+ monotonic_clock::epoch() + chrono::milliseconds(10150) +
+ factory.network_delay());
+ ASSERT_EQ(pi2_reliable_fetcher->value(), 99);
+
+ // TODO(austin): Verify that the dropped packet count increases.
+
+ ASSERT_FALSE(pi2_reliable_fetcher.Fetch());
+}
+
+// Tests that if we disconnect while a message is in various states of being
+// queued, it gets either dropped or sent as expected.
+TEST_F(SimulatedEventLoopDisconnectTest, MessageInFlightDuringDisconnect) {
+ time.StartEqual();
+ factory.SkipTimingReport();
+ factory.DisableStatistics();
+
+ NodeEventLoopFactory *pi1 = factory.GetNodeEventLoopFactory("pi1");
+ NodeEventLoopFactory *pi2 = factory.GetNodeEventLoopFactory("pi2");
+
+ std::unique_ptr<aos::EventLoop> pi1_event_loop = pi1->MakeEventLoop("sender");
+
+ std::unique_ptr<aos::EventLoop> pi2_event_loop =
+ pi2->MakeEventLoop("fetcher");
+ aos::Fetcher<examples::Ping> fetcher =
+ pi2_event_loop->MakeFetcher<examples::Ping>("/unreliable");
+
+ ASSERT_FALSE(fetcher.Fetch());
+
+ aos::monotonic_clock::time_point now = pi1_event_loop->monotonic_now();
+ {
+ FunctionScheduler run_at(pi1_event_loop.get());
+ aos::Sender<examples::Ping> pi1_sender =
+ pi1_event_loop->MakeSender<examples::Ping>("/unreliable");
+
+ int i = 0;
+ for (const std::chrono::nanoseconds dt :
+ {chrono::microseconds(5000), chrono::microseconds(1),
+ chrono::microseconds(2), chrono::microseconds(70),
+ chrono::microseconds(63), chrono::microseconds(140),
+ chrono::microseconds(160)}) {
+ run_at.ScheduleAt(
+ [&]() {
+ pi1->Connect(pi2->node());
+ pi2->Connect(pi1->node());
+ },
+ now);
+
+ now += chrono::milliseconds(100);
+
+ run_at.ScheduleAt([&, i = i]() { SendPing(&pi1_sender, i); }, now);
+
+ now += dt;
+
+ run_at.ScheduleAt(
+ [&]() {
+ // Fully disconnect the nodes.
+ pi1->Disconnect(pi2->node());
+ pi2->Disconnect(pi1->node());
+ },
+ now);
+
+ now += chrono::milliseconds(100) - dt;
+ ++i;
+ }
+
+ factory.RunFor(now - pi1_event_loop->monotonic_now());
+ }
+
+ const monotonic_clock::time_point e = monotonic_clock::epoch();
+ const chrono::nanoseconds send_delay = factory.send_delay();
+ const chrono::nanoseconds network_delay = factory.network_delay();
+
+ const std::vector<ExpectedTimestamps> expected_values = {
+ ExpectedTimestamps{
+ .remote_time = e + chrono::milliseconds(100),
+ .remote_transmit_time = e + chrono::milliseconds(100) + send_delay,
+ .event_time =
+ e + chrono::milliseconds(100) + send_delay + network_delay,
+ .value = 0,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::milliseconds(1300),
+ .remote_transmit_time = e + chrono::milliseconds(1300) + send_delay,
+ .event_time =
+ e + chrono::milliseconds(1300) + send_delay + network_delay,
+ .value = 6,
+ },
+ };
+
+ for (const ExpectedTimestamps value : expected_values) {
+ ASSERT_TRUE(fetcher.FetchNext());
+ EXPECT_EQ(fetcher.context().monotonic_remote_time, value.remote_time);
+ EXPECT_EQ(fetcher.context().monotonic_remote_transmit_time,
+ value.remote_transmit_time);
+ EXPECT_EQ(fetcher.context().monotonic_event_time, value.event_time);
+ EXPECT_EQ(fetcher->value(), value.value);
+ }
+
+ // TODO(austin): Verify that the dropped packet count increases.
+
+ ASSERT_FALSE(fetcher.Fetch());
+}
+
+class PingLogger {
+ public:
+ PingLogger(aos::EventLoop *event_loop, std::string_view channel,
+ std::vector<std::pair<aos::Context, int>> *msgs)
+ : event_loop_(event_loop),
+ fetcher_(event_loop_->MakeFetcher<examples::Ping>(channel)),
+ msgs_(msgs) {
+ event_loop_->OnRun([this]() { CHECK(!fetcher_.Fetch()); });
+ }
+
+ ~PingLogger() {
+ while (fetcher_.FetchNext()) {
+ msgs_->emplace_back(fetcher_.context(), fetcher_->value());
+ }
+ }
+
+ private:
+ aos::EventLoop *event_loop_;
+ aos::Fetcher<examples::Ping> fetcher_;
+ std::vector<std::pair<aos::Context, int>> *msgs_;
+};
+
+// Tests that rebooting while a message is in flight works as expected.
+TEST_F(SimulatedEventLoopDisconnectTest, MessageInFlightDuringReboot) {
+ time.StartEqual();
+ for (int i = 0; i < 8; ++i) {
+ time.RebootAt(1, distributed_clock::epoch() + chrono::seconds(10 * i));
+ }
+
+ factory.SkipTimingReport();
+ factory.DisableStatistics();
+
+ NodeEventLoopFactory *pi1 = factory.GetNodeEventLoopFactory("pi1");
+ NodeEventLoopFactory *pi2 = factory.GetNodeEventLoopFactory("pi2");
+
+ std::unique_ptr<aos::EventLoop> pi1_event_loop = pi1->MakeEventLoop("sender");
+
+ aos::monotonic_clock::time_point now = pi1_event_loop->monotonic_now();
+ FunctionScheduler run_at(pi1_event_loop.get());
+ aos::Sender<examples::Ping> pi1_sender =
+ pi1_event_loop->MakeSender<examples::Ping>("/unreliable");
+
+ int i = 0;
+ for (const std::chrono::nanoseconds dt :
+ {chrono::microseconds(5000), chrono::microseconds(1),
+ chrono::microseconds(2), chrono::microseconds(70),
+ chrono::microseconds(63), chrono::microseconds(140),
+ chrono::microseconds(160)}) {
+ run_at.ScheduleAt([&, i = i]() { SendPing(&pi1_sender, i); },
+ now + chrono::seconds(10) - dt);
+
+ now += chrono::seconds(10);
+ ++i;
+ }
+
+ std::vector<std::pair<aos::Context, int>> msgs;
+
+ pi2->OnStartup([pi2, &msgs]() {
+ pi2->AlwaysStart<PingLogger>("ping_logger", "/unreliable", &msgs);
+ });
+
+ factory.RunFor(now - pi1_event_loop->monotonic_now() + chrono::seconds(10));
+
+ const monotonic_clock::time_point e = monotonic_clock::epoch();
+ const chrono::nanoseconds send_delay = factory.send_delay();
+ const chrono::nanoseconds network_delay = factory.network_delay();
+
+ const std::vector<ExpectedTimestamps> expected_values = {
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(9995000),
+ .remote_transmit_time =
+ e + chrono::microseconds(9995000) + send_delay,
+ .event_time =
+ e + chrono::microseconds(9995000) + send_delay + network_delay,
+ .value = 0,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(19999999),
+ .remote_transmit_time =
+ e + chrono::microseconds(19999999) + send_delay,
+ .event_time =
+ e + chrono::microseconds(-1) + send_delay + network_delay,
+ .value = 1,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(29999998),
+ .remote_transmit_time =
+ e + chrono::microseconds(29999998) + send_delay,
+ .event_time =
+ e + chrono::microseconds(-2) + send_delay + network_delay,
+ .value = 2,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(69999840),
+ .remote_transmit_time =
+ e + chrono::microseconds(69999840) + send_delay,
+ .event_time =
+ e + chrono::microseconds(9999840) + send_delay + network_delay,
+ .value = 6,
+ },
+ };
+
+ ASSERT_EQ(msgs.size(), expected_values.size());
+
+ for (size_t i = 0; i < msgs.size(); ++i) {
+ EXPECT_EQ(msgs[i].first.monotonic_remote_time,
+ expected_values[i].remote_time);
+ EXPECT_EQ(msgs[i].first.monotonic_remote_transmit_time,
+ expected_values[i].remote_transmit_time);
+ EXPECT_EQ(msgs[i].first.monotonic_event_time,
+ expected_values[i].event_time);
+ EXPECT_EQ(msgs[i].second, expected_values[i].value);
+ }
+
+ // TODO(austin): Verify that the dropped packet count increases.
+}
+
+// Tests that rebooting while a message is in flight works as expected.
+TEST_F(SimulatedEventLoopDisconnectTest, ReliableMessageInFlightDuringReboot) {
+ time.StartEqual();
+ for (int i = 0; i < 8; ++i) {
+ time.RebootAt(1, distributed_clock::epoch() + chrono::seconds(10 * i));
+ }
+
+ factory.SkipTimingReport();
+ factory.DisableStatistics();
+
+ NodeEventLoopFactory *pi1 = factory.GetNodeEventLoopFactory("pi1");
+ NodeEventLoopFactory *pi2 = factory.GetNodeEventLoopFactory("pi2");
+
+ std::unique_ptr<aos::EventLoop> pi1_event_loop = pi1->MakeEventLoop("sender");
+
+ aos::monotonic_clock::time_point now = pi1_event_loop->monotonic_now();
+ FunctionScheduler run_at(pi1_event_loop.get());
+ aos::Sender<examples::Ping> pi1_sender =
+ pi1_event_loop->MakeSender<examples::Ping>("/reliable");
+
+ int i = 0;
+ for (const std::chrono::nanoseconds dt :
+ {chrono::microseconds(5000), chrono::microseconds(1),
+ chrono::microseconds(2), chrono::microseconds(70),
+ chrono::microseconds(63), chrono::microseconds(140),
+ chrono::microseconds(160)}) {
+ run_at.ScheduleAt([&, i = i]() { SendPing(&pi1_sender, i); },
+ now + chrono::seconds(10) - dt);
+
+ now += chrono::seconds(10);
+ ++i;
+ }
+
+ std::vector<std::pair<aos::Context, int>> msgs;
+
+ PingLogger *logger;
+ pi2->OnStartup([pi2, &msgs, &logger]() {
+ logger = pi2->AlwaysStart<PingLogger>("ping_logger", "/reliable", &msgs);
+ });
+
+ factory.RunFor(now - pi1_event_loop->monotonic_now() + chrono::seconds(10));
+
+ // Stop the logger to flush the last boot of data.
+ pi2->Stop(logger);
+
+ const monotonic_clock::time_point e = monotonic_clock::epoch();
+ const chrono::nanoseconds send_delay = factory.send_delay();
+ const chrono::nanoseconds network_delay = factory.network_delay();
+
+ // Verified using --vmodule=simulated_event_loop=1 and looking at the actual
+ // event times to confirm what should have been forwarded when.
+ const std::vector<ExpectedTimestamps> expected_values = {
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(9995000),
+ .remote_transmit_time =
+ e + chrono::microseconds(9995000) + send_delay,
+ .event_time =
+ e + chrono::microseconds(9995000) + send_delay + network_delay,
+ .value = 0,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(9995000),
+ .remote_transmit_time = e + chrono::microseconds(10000000),
+ .event_time = e + network_delay,
+ .value = 0,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(19999999),
+ .remote_transmit_time = e + chrono::microseconds(20000000),
+ .event_time = e + network_delay,
+ .value = 1,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(29999998),
+ .remote_transmit_time = e + chrono::microseconds(30000000),
+ .event_time = e + network_delay,
+ .value = 2,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(39999930),
+ .remote_transmit_time = e + chrono::microseconds(40000000),
+ .event_time = e + network_delay,
+ .value = 3,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(49999937),
+ .remote_transmit_time = e + chrono::microseconds(50000000),
+ .event_time = e + network_delay,
+ .value = 4,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(59999860),
+ .remote_transmit_time = e + chrono::microseconds(60000000),
+ .event_time = e + network_delay,
+ .value = 5,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(69999840),
+ .remote_transmit_time = e + chrono::microseconds(69999890),
+ .event_time = e + chrono::microseconds(9999890) + network_delay,
+ .value = 6,
+ },
+ ExpectedTimestamps{
+ .remote_time = e + chrono::microseconds(69999840),
+ .remote_transmit_time = e + chrono::microseconds(70000000),
+ .event_time = e + network_delay,
+ .value = 6,
+ },
+ };
+
+ ASSERT_EQ(msgs.size(), expected_values.size());
+
+ for (size_t i = 0; i < msgs.size(); ++i) {
+ EXPECT_EQ(msgs[i].first.monotonic_remote_time,
+ expected_values[i].remote_time);
+ EXPECT_EQ(msgs[i].first.monotonic_remote_transmit_time,
+ expected_values[i].remote_transmit_time);
+ EXPECT_EQ(msgs[i].first.monotonic_event_time,
+ expected_values[i].event_time);
+ EXPECT_EQ(msgs[i].second, expected_values[i].value);
+ }
+
+ // TODO(austin): Verify that the dropped packet count increases.
+}
+
} // namespace aos::testing
diff --git a/aos/events/simulated_network_bridge.cc b/aos/events/simulated_network_bridge.cc
index 5c97292..66c902f 100644
--- a/aos/events/simulated_network_bridge.cc
+++ b/aos/events/simulated_network_bridge.cc
@@ -1,6 +1,7 @@
#include "aos/events/simulated_network_bridge.h"
#include "absl/strings/str_cat.h"
+#include "glog/logging.h"
#include "aos/configuration.h"
#include "aos/events/event_loop.h"
@@ -12,8 +13,10 @@
// This class delays messages forwarded between two factories.
//
// The basic design is that we need to use the distributed_clock to convert
-// monotonic times from the source to the destination node. We also use a
-// fetcher to manage the queue of data, and a timer to schedule the sends.
+// monotonic times from the source to the destination node. We use a list of
+// timestamps added each time a message is delivered to the server side to drive
+// the client side publishing. This pulls the data from the fetcher to match
+// with the timestamps queued.
class RawMessageDelayer {
public:
RawMessageDelayer(const Channel *channel, const Connection *connection,
@@ -41,7 +44,8 @@
void SetFetchEventLoop(aos::EventLoop *fetch_event_loop,
MessageBridgeServerStatus *server_status,
ChannelTimestampSender *timestamp_loggers) {
- sent_ = false;
+ // Clear out state when the source node restarts.
+ last_sent_ = TransmitTime();
fetch_event_loop_ = fetch_event_loop;
if (fetch_event_loop_) {
fetcher_ = fetch_event_loop_->MakeRawFetcher(channel_);
@@ -50,7 +54,7 @@
}
server_status_ = server_status;
- if (server_status) {
+ if (server_status_) {
server_connection_ =
server_status_->FindServerConnection(send_node_factory_->node());
server_index_ = configuration::GetNodeIndex(
@@ -84,7 +88,6 @@
void SetSendEventLoop(aos::EventLoop *send_event_loop,
MessageBridgeClientStatus *client_status) {
- sent_ = false;
send_event_loop_ = send_event_loop;
if (send_event_loop_ && !forwarding_disabled_) {
sender_ = send_event_loop_->MakeRawSender(channel_);
@@ -121,147 +124,264 @@
const Channel *channel() const { return channel_; }
- uint32_t time_to_live() {
+ // Returns true if the connection is reliable.
+ bool reliable() const { return time_to_live() == 0; }
+
+ uint32_t time_to_live() const {
return configuration::ConnectionToNode(channel_, send_node_factory_->node())
->time_to_live();
}
- void ScheduleReliable() {
- if (forwarding_disabled()) return;
+ std::string Name() const {
+ std::string result;
+ result +=
+ (fetch_event_loop_ ? fetch_event_loop_->node()->name()->string_view()
+ : std::string_view("?"));
+ result += " -> ";
+ result +=
+ (send_event_loop_ ? send_event_loop_->node()->name()->string_view()
+ : std::string_view("?"));
+ result += " ";
+ result += aos::configuration::StrippedChannelToString(channel());
+ return result;
+ }
+ // Schedules forwarding any reliable messages when a node boots.
+ void ScheduleReliable() {
+ if (forwarding_disabled()) {
+ return;
+ }
+
+ // There is no sending side awake, don't do work.
if (!fetcher_) {
return;
}
- if (fetcher_->context().data == nullptr || sent_) {
- sent_ = !fetcher_->Fetch();
- }
- FetchNext();
- if (fetcher_->context().data == nullptr || sent_) {
+ // The network connection is disconnected, forget about this message. If
+ // this is a reliable message, it will get picked up in Connect() so we
+ // don't need to follow it here.
+ if (server_connection_->state() != State::CONNECTED) {
return;
}
- // Send at startup. It is the best we can do.
- const monotonic_clock::time_point monotonic_delivered_time =
- send_node_factory_->monotonic_now() +
- send_node_factory_->network_delay();
+ // If there is no receiving side, bail.
+ if (!timer_) {
+ return;
+ }
- CHECK_GE(monotonic_delivered_time, send_node_factory_->monotonic_now())
+ // We only want the newest message, grab it and see if there's anything to
+ // do.
+ fetcher_->Fetch();
+
+ // No data, bail.
+ if (fetcher_->context().data == nullptr) {
+ return;
+ }
+
+ // Now, we know we've got a message we need to deliver, mark it down.
+ QueueMessage(fetcher_->context().queue_index,
+ fetcher_->context().monotonic_event_time,
+ fetch_event_loop_->monotonic_now());
+
+ // Send at startup. It is the best we can do.
+ const logger::BootTimestamp monotonic_delivery_time =
+ DeliveredTime(monotonic_remote_transmit_times_.front().transmit_time);
+
+ // This can only happen if a node reboots in under 100 uS. That's crazy,
+ // CHECK for now and handle it if someone actually has a good need.
+ CHECK_EQ(monotonic_delivery_time.boot, send_node_factory_->boot_count());
+
+ CHECK_GE(monotonic_delivery_time.time, send_node_factory_->monotonic_now())
<< ": Trying to deliver message in the past on channel "
<< configuration::StrippedChannelToString(fetcher_->channel())
<< " to node " << send_event_loop_->node()->name()->string_view()
<< " sent from " << fetcher_->channel()->source_node()->string_view()
<< " at " << fetch_node_factory_->monotonic_now();
- if (timer_) {
+ if (!timer_scheduled_) {
server_status_->AddSentPacket(server_index_, channel_);
- timer_->Schedule(monotonic_delivered_time);
+ timer_->Schedule(monotonic_delivery_time.time);
timer_scheduled_ = true;
- } else {
- server_status_->AddDroppedPacket(server_index_, channel_);
- sent_ = true;
}
}
- bool timer_scheduled_ = false;
+ // Handles a message begin delivered to message_bridge_server, and either
+ // drops it or queues it up.
+ void MessageWatcherCallback(uint32_t sent_queue_index,
+ monotonic_clock::time_point monotonic_sent_time,
+ monotonic_clock::time_point transmit_time) {
+ if (server_connection_->state() != State::CONNECTED) {
+ server_status_->AddDroppedPacket(server_index_, channel_);
+ return;
+ }
+
+ QueueMessage(sent_queue_index, monotonic_sent_time, transmit_time);
+ Schedule();
+ }
+
+ void QueueMessage(uint32_t sent_queue_index,
+ monotonic_clock::time_point monotonic_sent_time,
+ monotonic_clock::time_point transmit_time) {
+ CHECK(!forwarding_disabled());
+
+ // When a reliable message gets queued, we can both receive the wakeup from
+ // the watcher, and from ScheduleReliable. In that case, detect that it is
+ // already in the queue and deduplicate with it.
+ if (monotonic_remote_transmit_times_.size() > 0u) {
+ const TransmitTime back = monotonic_remote_transmit_times_
+ [monotonic_remote_transmit_times_.size() - 1];
+ if (back.sent_queue_index == sent_queue_index) {
+ CHECK_EQ(back.monotonic_sent_time, monotonic_sent_time) << this;
+ CHECK(reliable());
+ CHECK_LE(back.transmit_time, transmit_time) << this;
+ return;
+ }
+ }
+
+ // Capture the time this message was published over the network on the
+ // remote node
+ monotonic_remote_transmit_times_.push_back(TransmitTime{
+ .monotonic_sent_time = monotonic_sent_time,
+ .sent_queue_index = sent_queue_index,
+ .transmit_time = transmit_time,
+ });
+ }
+
+ // Handles this node connecting to the network.
+ void Connect() {
+ CHECK(fetcher_);
+
+ // We only send the last message. Point the fetcher to the latest to handle
+ // getting too far behind.
+ fetcher_->Fetch();
+
+ // Unreliable messages aren't resent on reconnect.
+ if (!reliable()) {
+ return;
+ }
+
+ if (forwarding_disabled()) {
+ return;
+ }
+
+ // Ignore it if there is no data.
+ if (fetcher_->context().data == nullptr) {
+ return;
+ }
+
+ // See if the newest message got sent already. If it hasn't, queue it up to
+ // be sent.
+ if (fetcher_->context().queue_index != last_sent_.sent_queue_index) {
+ QueueMessage(fetcher_->context().queue_index,
+ fetcher_->context().monotonic_event_time,
+ fetch_event_loop_->monotonic_now());
+ }
+
+ Schedule();
+ }
+
+ // Returns true if we know that this connection sends to the destination node.
+ // Returns false if the destination hasn't been constructed.
+ bool SendingTo(const Node *destination) {
+ return send_event_loop_ && send_event_loop_->node() == destination;
+ }
// Kicks us to re-fetch and schedule the timer.
void Schedule() {
CHECK(!forwarding_disabled());
+ // Can't receive, bail.
if (!fetcher_) {
return;
}
+
+ // Already scheduled, nothing to see here.
if (timer_scheduled_) {
return;
}
- FetchNext();
- if (fetcher_->context().data == nullptr || sent_) {
+
+ // We've finally caught up, nothing to do.
+ if (monotonic_remote_transmit_times_.empty()) {
return;
}
- // Compute the time to publish this message.
- const monotonic_clock::time_point monotonic_delivered_time =
- DeliveredTime(fetcher_->context());
+ const monotonic_clock::time_point transmit_time =
+ monotonic_remote_transmit_times_[0].transmit_time;
- CHECK_GE(monotonic_delivered_time, send_node_factory_->monotonic_now())
- << ": Trying to deliver message in the past on channel "
+ // Compute the time to publish this message.
+ const logger::BootTimestamp monotonic_delivery_time =
+ DeliveredTime(transmit_time);
+
+ // This should be published after the reboot. Forget about it.
+ if (monotonic_delivery_time.boot != send_node_factory_->boot_count()) {
+ CHECK_GT(monotonic_delivery_time.boot, send_node_factory_->boot_count());
+
+ monotonic_remote_transmit_times_.erase(
+ monotonic_remote_transmit_times_.begin());
+ CHECK(monotonic_remote_transmit_times_.empty());
+ return;
+ }
+
+ CHECK_GE(monotonic_delivery_time.time, send_node_factory_->monotonic_now())
+ << ": " << this << " Trying to deliver message in the past on channel "
<< configuration::StrippedChannelToString(fetcher_->channel())
<< " to node " << send_event_loop_->node()->name()->string_view()
<< " sent from " << fetcher_->channel()->source_node()->string_view()
<< " at " << fetch_node_factory_->monotonic_now();
- if (timer_) {
- server_status_->AddSentPacket(server_index_, channel_);
- timer_->Schedule(monotonic_delivered_time);
- timer_scheduled_ = true;
- } else {
- server_status_->AddDroppedPacket(server_index_, channel_);
- sent_ = true;
- Schedule();
- }
+ CHECK(timer_);
+ server_status_->AddSentPacket(server_index_, channel_);
+ timer_->Schedule(monotonic_delivery_time.time);
+ timer_scheduled_ = true;
}
private:
- void FetchNext() {
- CHECK(server_connection_);
- // Keep pulling messages out of the fetcher until we find one in the future.
- while (true) {
- if (fetcher_->context().data == nullptr || sent_) {
- sent_ = !fetcher_->FetchNext();
- }
- if (sent_) {
- break;
- }
-
- if (server_connection_->state() != State::CONNECTED) {
- sent_ = true;
- server_status_->AddDroppedPacket(server_index_, channel_);
- continue;
- }
-
- if (fetcher_->context().monotonic_event_time +
- send_node_factory_->network_delay() +
- send_node_factory_->send_delay() >
- fetch_node_factory_->monotonic_now() ||
- time_to_live() == 0) {
- break;
- }
-
- // TODO(austin): Not cool. We want to actually forward these. This means
- // we need a more sophisticated concept of what is running.
- // TODO(james): This fails if multiple messages are sent on the same
- // channel within the same callback.
- LOG(WARNING) << "Not forwarding message on "
- << configuration::CleanedChannelToString(fetcher_->channel())
- << " because we aren't running. Sent at "
- << fetcher_->context().monotonic_event_time << " now is "
- << fetch_node_factory_->monotonic_now();
- sent_ = true;
- server_status_->AddDroppedPacket(server_index_, channel_);
- }
- }
-
// Actually sends the message, and reschedules.
void Send() {
timer_scheduled_ = false;
+
CHECK(sender_);
CHECK(client_status_);
+ CHECK(fetcher_);
+
+ CHECK(!monotonic_remote_transmit_times_.empty());
+ while (fetcher_->context().queue_index !=
+ monotonic_remote_transmit_times_.front().sent_queue_index) {
+ if (!fetcher_->FetchNext()) {
+ break;
+ }
+ }
+
+ // Confirm that the first element in the times list is ours, and pull the
+ // transmit time out of it.
+ CHECK_EQ(monotonic_remote_transmit_times_[0].monotonic_sent_time,
+ fetcher_->context().monotonic_event_time);
+ CHECK_EQ(monotonic_remote_transmit_times_[0].sent_queue_index,
+ fetcher_->context().queue_index);
+
+ const TransmitTime timestamp = monotonic_remote_transmit_times_[0];
+
+ monotonic_remote_transmit_times_.erase(
+ monotonic_remote_transmit_times_.begin());
+
if (server_connection_->state() != State::CONNECTED) {
- sent_ = true;
Schedule();
return;
}
+
// Fill out the send times.
sender_->CheckOk(sender_->Send(
fetcher_->context().data, fetcher_->context().size,
fetcher_->context().monotonic_event_time,
- fetcher_->context().realtime_event_time,
+ fetcher_->context().realtime_event_time, timestamp.transmit_time,
fetcher_->context().queue_index, fetcher_->context().source_boot_uuid));
+ // Record that this got sent.
+ last_sent_ = timestamp;
+
// And simulate message_bridge's offset recovery.
- client_status_->SampleFilter(
- client_index_, fetcher_->context().monotonic_event_time,
- sender_->monotonic_sent_time(), fetcher_->context().source_boot_uuid);
+ client_status_->SampleFilter(client_index_, timestamp.transmit_time,
+ sender_->monotonic_sent_time(),
+ fetcher_->context().source_boot_uuid);
client_connection_->mutate_received_packets(
client_connection_->received_packets() + 1);
@@ -294,7 +414,8 @@
fetcher_->context().realtime_event_time.time_since_epoch().count());
message_header_builder.add_remote_queue_index(
fetcher_->context().queue_index);
-
+ message_header_builder.add_monotonic_remote_transmit_time(
+ timestamp.transmit_time.time_since_epoch().count());
message_header_builder.add_monotonic_sent_time(
sender_->monotonic_sent_time().time_since_epoch().count());
message_header_builder.add_realtime_sent_time(
@@ -311,7 +432,6 @@
ScheduleTimestamp();
}
- sent_ = true;
Schedule();
}
@@ -354,15 +474,14 @@
}
// Converts from time on the sending node to time on the receiving node.
- monotonic_clock::time_point DeliveredTime(const Context &context) const {
+ logger::BootTimestamp DeliveredTime(
+ const monotonic_clock::time_point transmit_time) const {
const distributed_clock::time_point distributed_sent_time =
- fetch_node_factory_->ToDistributedClock(context.monotonic_event_time);
+ fetch_node_factory_->ToDistributedClock(transmit_time);
const logger::BootTimestamp t = send_node_factory_->FromDistributedClock(
- distributed_sent_time + send_node_factory_->network_delay() +
- send_node_factory_->send_delay());
- CHECK_EQ(t.boot, send_node_factory_->boot_count());
- return t.time;
+ distributed_sent_time + send_node_factory_->network_delay());
+ return t;
}
const Channel *channel_;
@@ -378,6 +497,7 @@
aos::EventLoop *send_event_loop_ = nullptr;
// Timer used to send.
aos::TimerHandler *timer_ = nullptr;
+ bool timer_scheduled_ = false;
// Timer used to send timestamps out.
aos::TimerHandler *timestamp_timer_ = nullptr;
// Time that the timer is scheduled for. Used to track if it needs to be
@@ -391,8 +511,6 @@
MessageBridgeServerStatus *server_status_ = nullptr;
const size_t destination_node_index_;
- // True if we have sent the message in the fetcher.
- bool sent_ = false;
ServerConnection *server_connection_ = nullptr;
int server_index_ = -1;
@@ -403,6 +521,20 @@
size_t channel_index_;
aos::Sender<RemoteMessage> *timestamp_logger_ = nullptr;
+ struct TransmitTime {
+ monotonic_clock::time_point monotonic_sent_time = monotonic_clock::min_time;
+ uint32_t sent_queue_index = 0xffffffff;
+ monotonic_clock::time_point transmit_time = monotonic_clock::min_time;
+ };
+
+ // Stores the time the message was handed to the kernel to be published on
+ // the remote node over the network for all forwarded relevant messages.
+ std::vector<TransmitTime> monotonic_remote_transmit_times_;
+
+ // Stores the last message which was published. This is used to know if we
+ // need to re-transmit something on reconnect or not.
+ TransmitTime last_sent_;
+
struct Timestamp {
Timestamp(FlatbufferDetachedBuffer<RemoteMessage> new_remote_message,
monotonic_clock::time_point new_monotonic_timestamp_time)
@@ -440,9 +572,9 @@
size_t node_index = 0;
for (const std::optional<MessageBridgeServerStatus::NodeState>
- &connection : node_state->server_status->nodes()) {
+ &connection : node_state->server_status_->nodes()) {
if (connection.has_value()) {
- node_state->server_status->ResetFilter(node_index);
+ node_state->server_status_->ResetFilter(node_index);
}
++node_index;
}
@@ -514,10 +646,14 @@
if (channel == timestamp_channel) {
source_event_loop->second.SetSendData(
- [captured_delayers = delayers.get()]() {
+ [source_event_loop, captured_delayers = delayers.get()](
+ uint32_t sent_queue_index,
+ monotonic_clock::time_point monotonic_sent_time) {
for (std::unique_ptr<RawMessageDelayer> &delayer :
captured_delayers->v) {
- delayer->Schedule();
+ delayer->MessageWatcherCallback(
+ sent_queue_index, monotonic_sent_time,
+ source_event_loop->second.event_loop->monotonic_now());
}
});
} else {
@@ -588,11 +724,21 @@
it->second.EnableStatistics();
}
+void SimulatedMessageBridge::State::MakeEventLoop() {
+ // Message bridge isn't the thing that should be catching sent-too-fast,
+ // and may need to be able to forward too-fast messages replayed from old
+ // logfiles.
+ SetEventLoop(node_factory_->MakeEventLoop(
+ "message_bridge", {NodeEventLoopFactory::CheckSentTooFast::kNo,
+ NodeEventLoopFactory::ExclusiveSenders::kNo,
+ {}}));
+}
+
void SimulatedMessageBridge::State::SetEventLoop(
std::unique_ptr<aos::EventLoop> loop) {
if (!loop) {
timestamp_loggers = ChannelTimestampSender(nullptr);
- server_status.reset();
+ server_status_.reset();
client_status.reset();
for (RawMessageDelayer *source_delayer : source_delayers_) {
source_delayer->SetFetchEventLoop(nullptr, nullptr, nullptr);
@@ -615,38 +761,42 @@
// Don't register watchers if we know we aren't forwarding.
if (watcher.second->disable_forwarding) continue;
event_loop->MakeRawNoArgWatcher(
- watcher.first, [captured_delayers = watcher.second](const Context &) {
+ watcher.first,
+ [this, captured_delayers = watcher.second](const Context &context) {
// We might get told after registering, so don't forward at that point
// too.
for (std::unique_ptr<RawMessageDelayer> &delayer :
captured_delayers->v) {
- delayer->Schedule();
+ delayer->MessageWatcherCallback(context.queue_index,
+ context.monotonic_event_time,
+ event_loop->monotonic_now());
}
});
}
timestamp_loggers = ChannelTimestampSender(event_loop.get());
- server_status = std::make_unique<MessageBridgeServerStatus>(event_loop.get());
+ server_status_ =
+ std::make_unique<MessageBridgeServerStatus>(event_loop.get());
if (disable_statistics_) {
- server_status->DisableStatistics(destroy_senders_ == DestroySenders::kYes);
+ server_status_->DisableStatistics(destroy_senders_ == DestroySenders::kYes);
}
{
size_t node_index = 0;
for (const std::optional<MessageBridgeServerStatus::NodeState> &connection :
- server_status->nodes()) {
+ server_status_->nodes()) {
if (connection.has_value()) {
if (boot_uuids_[node_index] != UUID::Zero()) {
switch (server_state_[node_index]) {
case message_bridge::State::DISCONNECTED:
- server_status->Disconnect(node_index);
+ server_status_->Disconnect(node_index);
break;
case message_bridge::State::CONNECTED:
- server_status->Connect(node_index, event_loop->monotonic_now());
+ server_status_->Connect(node_index, event_loop->monotonic_now());
break;
}
} else {
- server_status->Disconnect(node_index);
+ server_status_->Disconnect(node_index);
}
}
++node_index;
@@ -655,11 +805,11 @@
for (size_t i = 0; i < boot_uuids_.size(); ++i) {
if (boot_uuids_[i] != UUID::Zero()) {
- server_status->SetBootUUID(i, boot_uuids_[i]);
+ server_status_->SetBootUUID(i, boot_uuids_[i]);
}
}
if (fn_) {
- server_status->set_send_data(fn_);
+ server_status_->set_send_data(fn_);
}
client_status = std::make_unique<MessageBridgeClientStatus>(event_loop.get());
if (disable_statistics_) {
@@ -724,7 +874,7 @@
}
for (RawMessageDelayer *source_delayer : source_delayers_) {
- source_delayer->SetFetchEventLoop(event_loop.get(), server_status.get(),
+ source_delayer->SetFetchEventLoop(event_loop.get(), server_status_.get(),
×tamp_loggers);
}
for (RawMessageDelayer *destination_delayer : destination_delayers_) {
@@ -733,7 +883,7 @@
}
event_loop->OnRun([this]() {
for (RawMessageDelayer *destination_delayer : destination_delayers_) {
- if (destination_delayer->time_to_live() == 0) {
+ if (destination_delayer->reliable()) {
destination_delayer->ScheduleReliable();
}
}
@@ -752,11 +902,117 @@
// the message, then that would trigger the watchers in the delayers.
// However, we so far have continued to support Sending while stopped....
for (RawMessageDelayer *source_delayer : source_delayers_) {
- if (source_delayer->time_to_live() == 0) {
+ if (source_delayer->reliable()) {
source_delayer->ScheduleReliable();
}
}
});
}
+void SimulatedMessageBridge::State::SetSendData(
+ std::function<void(uint32_t, monotonic_clock::time_point)> fn) {
+ CHECK(!fn_);
+ fn_ = std::move(fn);
+ if (server_status_) {
+ server_status_->set_send_data(fn_);
+ }
+}
+
+void SimulatedMessageBridge::State::SetBootUUID(size_t node_index,
+ const UUID &boot_uuid) {
+ boot_uuids_[node_index] = boot_uuid;
+ const Node *node = node_factory_->configuration()->nodes()->Get(node_index);
+ if (server_status_) {
+ ServerConnection *connection = server_status_->FindServerConnection(node);
+ if (connection) {
+ if (boot_uuid == UUID::Zero()) {
+ server_status_->Disconnect(node_index);
+ server_status_->ResetFilter(node_index);
+ } else {
+ switch (server_state_[node_index]) {
+ case message_bridge::State::DISCONNECTED:
+ server_status_->Disconnect(node_index);
+ break;
+ case message_bridge::State::CONNECTED:
+ server_status_->Connect(node_index, event_loop->monotonic_now());
+ break;
+ }
+ server_status_->ResetFilter(node_index);
+ server_status_->SetBootUUID(node_index, boot_uuid);
+ }
+ }
+ }
+ if (client_status) {
+ const int client_index =
+ client_status->FindClientIndex(node->name()->string_view());
+ client_status->SampleReset(client_index);
+ if (boot_uuid == UUID::Zero()) {
+ client_status->Disconnect(client_index);
+ } else {
+ switch (client_state_[node_index]) {
+ case message_bridge::State::CONNECTED:
+ client_status->Connect(client_index);
+ break;
+ case message_bridge::State::DISCONNECTED:
+ client_status->Disconnect(client_index);
+ break;
+ }
+ }
+ }
+}
+
+void SimulatedMessageBridge::State::SetServerState(
+ const Node *destination, message_bridge::State state) {
+ const size_t node_index =
+ configuration::GetNodeIndex(node_factory_->configuration(), destination);
+ server_state_[node_index] = state;
+ if (server_status_) {
+ ServerConnection *connection =
+ server_status_->FindServerConnection(destination);
+ if (connection == nullptr) return;
+
+ if (state == connection->state()) {
+ return;
+ }
+ switch (state) {
+ case message_bridge::State::DISCONNECTED:
+ server_status_->Disconnect(node_index);
+ break;
+ case message_bridge::State::CONNECTED:
+ server_status_->Connect(node_index, event_loop->monotonic_now());
+ for (RawMessageDelayer *delayer : source_delayers_) {
+ if (delayer->SendingTo(destination)) {
+ delayer->Connect();
+ }
+ }
+ break;
+ }
+ }
+}
+
+void SimulatedMessageBridge::State::SetClientState(
+ const Node *source, message_bridge::State state) {
+ const size_t node_index =
+ configuration::GetNodeIndex(node_factory_->configuration(), source);
+ client_state_[node_index] = state;
+ if (client_status) {
+ const int client_index =
+ client_status->FindClientIndex(source->name()->string_view());
+ ClientConnection *connection = client_status->GetClientConnection(source);
+
+ // TODO(austin): Are there cases where we want to dedup 2 CONNECTED
+ // calls?
+ if (connection->state() != state) {
+ switch (state) {
+ case message_bridge::State::CONNECTED:
+ client_status->Connect(client_index);
+ break;
+ case message_bridge::State::DISCONNECTED:
+ client_status->Disconnect(client_index);
+ break;
+ }
+ }
+ }
+}
+
} // namespace aos::message_bridge
diff --git a/aos/events/simulated_network_bridge.h b/aos/events/simulated_network_bridge.h
index 14a7321..e850396 100644
--- a/aos/events/simulated_network_bridge.h
+++ b/aos/events/simulated_network_bridge.h
@@ -59,9 +59,9 @@
void DisableStatistics(DestroySenders destroy_senders) {
disable_statistics_ = true;
destroy_senders_ = destroy_senders;
- if (server_status) {
- server_status->DisableStatistics(destroy_senders ==
- DestroySenders::kYes);
+ if (server_status_) {
+ server_status_->DisableStatistics(destroy_senders ==
+ DestroySenders::kYes);
}
if (client_status) {
client_status->DisableStatistics(destroy_senders ==
@@ -71,8 +71,8 @@
void EnableStatistics() {
disable_statistics_ = false;
- if (server_status) {
- server_status->EnableStatistics();
+ if (server_status_) {
+ server_status_->EnableStatistics();
}
if (client_status) {
client_status->EnableStatistics();
@@ -86,121 +86,22 @@
destination_delayers_.emplace_back(delayer);
}
- void MakeEventLoop() {
- // Message bridge isn't the thing that should be catching sent-too-fast,
- // and may need to be able to forward too-fast messages replayed from old
- // logfiles.
- SetEventLoop(node_factory_->MakeEventLoop(
- "message_bridge", {NodeEventLoopFactory::CheckSentTooFast::kNo,
- NodeEventLoopFactory::ExclusiveSenders::kNo,
- {}}));
- }
+ void MakeEventLoop();
void SetEventLoop(std::unique_ptr<aos::EventLoop> loop);
- void SetSendData(std::function<void()> fn) {
- CHECK(!fn_);
- fn_ = std::move(fn);
- if (server_status) {
- server_status->set_send_data(fn_);
- }
- }
+ void SetSendData(
+ std::function<void(uint32_t, monotonic_clock::time_point)> fn);
void AddDelayerWatcher(const Channel *channel, DelayersVector *v) {
delayer_watchers_.emplace_back(channel, v);
}
- void SetBootUUID(size_t node_index, const UUID &boot_uuid) {
- boot_uuids_[node_index] = boot_uuid;
- const Node *node =
- node_factory_->configuration()->nodes()->Get(node_index);
- if (server_status) {
- ServerConnection *connection =
- server_status->FindServerConnection(node);
- if (connection) {
- if (boot_uuid == UUID::Zero()) {
- server_status->Disconnect(node_index);
- server_status->ResetFilter(node_index);
- } else {
- switch (server_state_[node_index]) {
- case message_bridge::State::DISCONNECTED:
- server_status->Disconnect(node_index);
- break;
- case message_bridge::State::CONNECTED:
- server_status->Connect(node_index, event_loop->monotonic_now());
- break;
- }
- server_status->ResetFilter(node_index);
- server_status->SetBootUUID(node_index, boot_uuid);
- }
- }
- }
- if (client_status) {
- const int client_index =
- client_status->FindClientIndex(node->name()->string_view());
- client_status->SampleReset(client_index);
- if (boot_uuid == UUID::Zero()) {
- client_status->Disconnect(client_index);
- } else {
- switch (client_state_[node_index]) {
- case message_bridge::State::CONNECTED:
- client_status->Connect(client_index);
- break;
- case message_bridge::State::DISCONNECTED:
- client_status->Disconnect(client_index);
- break;
- }
- }
- }
- }
+ void SetBootUUID(size_t node_index, const UUID &boot_uuid);
- void SetServerState(const Node *destination, message_bridge::State state) {
- const size_t node_index = configuration::GetNodeIndex(
- node_factory_->configuration(), destination);
- server_state_[node_index] = state;
- if (server_status) {
- ServerConnection *connection =
- server_status->FindServerConnection(destination);
- if (connection == nullptr) return;
+ void SetServerState(const Node *destination, message_bridge::State state);
- if (state == connection->state()) {
- return;
- }
- switch (state) {
- case message_bridge::State::DISCONNECTED:
- server_status->Disconnect(node_index);
- break;
- case message_bridge::State::CONNECTED:
- server_status->Connect(node_index, event_loop->monotonic_now());
- break;
- }
- }
- }
-
- void SetClientState(const Node *source, message_bridge::State state) {
- const size_t node_index =
- configuration::GetNodeIndex(node_factory_->configuration(), source);
- client_state_[node_index] = state;
- if (client_status) {
- const int client_index =
- client_status->FindClientIndex(source->name()->string_view());
- ClientConnection *connection =
- client_status->GetClientConnection(source);
-
- // TODO(austin): Are there cases where we want to dedup 2 CONNECTED
- // calls?
- if (connection->state() != state) {
- switch (state) {
- case message_bridge::State::CONNECTED:
- client_status->Connect(client_index);
- break;
- case message_bridge::State::DISCONNECTED:
- client_status->Disconnect(client_index);
- break;
- }
- }
- }
- }
+ void SetClientState(const Node *source, message_bridge::State state);
std::vector<UUID> boot_uuids_;
std::vector<message_bridge::State> client_state_;
@@ -208,12 +109,12 @@
std::vector<std::pair<const Channel *, DelayersVector *>> delayer_watchers_;
- std::function<void()> fn_;
+ std::function<void(uint32_t, monotonic_clock::time_point)> fn_;
NodeEventLoopFactory *node_factory_;
std::unique_ptr<aos::EventLoop> event_loop;
ChannelTimestampSender timestamp_loggers;
- std::unique_ptr<MessageBridgeServerStatus> server_status;
+ std::unique_ptr<MessageBridgeServerStatus> server_status_;
std::unique_ptr<MessageBridgeClientStatus> client_status;
// List of delayers to update whenever this node starts or stops.
diff --git a/aos/flatbuffer_introspection.cc b/aos/flatbuffer_introspection.cc
index 4dd7f2a..163c281 100644
--- a/aos/flatbuffer_introspection.cc
+++ b/aos/flatbuffer_introspection.cc
@@ -61,7 +61,7 @@
void FloatToString(double val, reflection::BaseType type,
FastStringBuilder *out) {
if (std::isnan(val)) {
- out->Append("null");
+ out->Append(std::signbit(val) ? "-nan" : "nan");
return;
}
switch (type) {
@@ -228,6 +228,9 @@
}
out->AppendChar('[');
+ if (!wrap) {
+ out->AppendChar(' ');
+ }
for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
if (i != 0) {
if (wrap) {
@@ -271,6 +274,8 @@
}
if (wrap) {
AddWrapping(out, tree_depth);
+ } else {
+ out->AppendChar(' ');
}
out->AppendChar(']');
} else {
@@ -327,6 +332,9 @@
}
out->AppendChar('{');
+ if (!wrap) {
+ out->AppendChar(' ');
+ }
for (const reflection::Field *field : *obj->fields()) {
// Check whether this object has the field populated (even for structs,
// which should have all fields populated)
@@ -355,6 +363,8 @@
if (wrap) {
AddWrapping(out, tree_depth);
+ } else {
+ out->AppendChar(' ');
}
out->AppendChar('}');
diff --git a/aos/flatbuffer_introspection_test.cc b/aos/flatbuffer_introspection_test.cc
index cc7d627..54c6f18 100644
--- a/aos/flatbuffer_introspection_test.cc
+++ b/aos/flatbuffer_introspection_test.cc
@@ -45,9 +45,9 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
EXPECT_EQ(out,
- "{\"foo_bool\": true, \"foo_byte\": -5, \"foo_int\": -20, "
+ "{ \"foo_bool\": true, \"foo_byte\": -5, \"foo_int\": -20, "
"\"foo_long\": -100, \"foo_short\": -10, \"foo_ubyte\": 5, "
- "\"foo_uint\": 20, \"foo_ulong\": 100, \"foo_ushort\": 10}");
+ "\"foo_uint\": 20, \"foo_ulong\": 100, \"foo_ushort\": 10 }");
}
TEST_F(FlatbufferIntrospectionTest, FloatTest) {
@@ -62,7 +62,7 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
EXPECT_EQ(out,
- "{\"foo_double\": 0.555555555555556, \"foo_float\": 0.333333}");
+ "{ \"foo_double\": 0.555555555555556, \"foo_float\": 0.333333 }");
}
TEST_F(FlatbufferIntrospectionTest, NanFloatTest) {
@@ -76,7 +76,7 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out, "{\"foo_double\": null, \"foo_float\": null}");
+ EXPECT_EQ(out, "{ \"foo_double\": nan, \"foo_float\": nan }");
}
TEST_F(FlatbufferIntrospectionTest, VectorScalarTest) {
@@ -129,18 +129,18 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(
- out,
- "{\"vector_foo_bool\": [true, false, true, false], \"vector_foo_byte\": "
- "[-3, -2, -1, 0, 1, 2, 3], \"vector_foo_double\": [0, 0.111111111111111, "
- "0.222222222222222, 0.333333333333333], \"vector_foo_float\": [0, "
- "0.111111, 0.222222, 0.333333], \"vector_foo_int\": [-300, -200, -100, "
- "0, 100, 200, 300], \"vector_foo_long\": [-3000, -2000, -1000, 0, 1000, "
- "2000, 3000], \"vector_foo_short\": [-30, -20, -10, 0, 10, 20, 30], "
- "\"vector_foo_ubyte\": [0, 1, 2, 3, 4, 5, 6], \"vector_foo_uint\": [0, "
- "100, 200, 300, 400, 500, 600], \"vector_foo_ulong\": [0, 1000, 2000, "
- "3000, 4000, 5000, 6000], \"vector_foo_ushort\": [0, 10, 20, 30, 40, 50, "
- "60]}");
+ EXPECT_EQ(out,
+ "{ \"vector_foo_bool\": [ true, false, true, false ], "
+ "\"vector_foo_byte\": [ -3, -2, -1, 0, 1, 2, 3 ], "
+ "\"vector_foo_double\": [ 0, 0.111111111111111, 0.222222222222222, "
+ "0.333333333333333 ], \"vector_foo_float\": [ 0, 0.111111, "
+ "0.222222, 0.333333 ], \"vector_foo_int\": [ -300, -200, -100, 0, "
+ "100, 200, 300 ], \"vector_foo_long\": [ -3000, -2000, -1000, 0, "
+ "1000, 2000, 3000 ], \"vector_foo_short\": [ -30, -20, -10, 0, 10, "
+ "20, 30 ], \"vector_foo_ubyte\": [ 0, 1, 2, 3, 4, 5, 6 ], "
+ "\"vector_foo_uint\": [ 0, 100, 200, 300, 400, 500, 600 ], "
+ "\"vector_foo_ulong\": [ 0, 1000, 2000, 3000, 4000, 5000, 6000 ], "
+ "\"vector_foo_ushort\": [ 0, 10, 20, 30, 40, 50, 60 ] }");
}
TEST_F(FlatbufferIntrospectionTest, StringTest) {
@@ -155,7 +155,7 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out, "{\"foo_string\": \"I <3 FlatBuffers!\"}");
+ EXPECT_EQ(out, "{ \"foo_string\": \"I <3 FlatBuffers!\" }");
}
TEST_F(FlatbufferIntrospectionTest, EnumTest) {
@@ -168,7 +168,7 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out, "{\"foo_enum\": \"UShort\"}");
+ EXPECT_EQ(out, "{ \"foo_enum\": \"UShort\" }");
}
TEST_F(FlatbufferIntrospectionTest, EnumWithUnknownValueTest) {
@@ -183,7 +183,7 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out, "{\"foo_enum\": 123}");
+ EXPECT_EQ(out, "{ \"foo_enum\": 123 }");
}
TEST_F(FlatbufferIntrospectionTest, VectorStringTest) {
@@ -221,10 +221,11 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out,
- "{\"vector_foo_string\": [\"abc\", \"acb\"], \"vov\": {\"v\": "
- "[{\"str\": [\"abc\", \"acb\"]}, {\"str\": [\"bac\", \"bca\"]}, "
- "{\"str\": [\"cab\", \"cba\"]}]}}");
+ EXPECT_EQ(
+ out,
+ "{ \"vector_foo_string\": [ \"abc\", \"acb\" ], \"vov\": { \"v\": "
+ "[ { \"str\": [ \"abc\", \"acb\" ] }, { \"str\": [ \"bac\", \"bca\" ] }, "
+ "{ \"str\": [ \"cab\", \"cba\" ] } ] } }");
}
TEST_F(FlatbufferIntrospectionTest, TableTest) {
@@ -254,10 +255,10 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
EXPECT_EQ(out,
- "{\"foo_byte\": 5, \"foo_string\": \"Root Config String\", "
- "\"nested_config\": {\"foo_byte\": 10, \"foo_string\": \"Nested "
- "Config String\", \"vector_foo_byte\": [6, 7, 8, 9, 10]}, "
- "\"vector_foo_byte\": [0, 1, 2, 3, 4, 5]}");
+ "{ \"foo_byte\": 5, \"foo_string\": \"Root Config String\", "
+ "\"nested_config\": { \"foo_byte\": 10, \"foo_string\": \"Nested "
+ "Config String\", \"vector_foo_byte\": [ 6, 7, 8, 9, 10 ] }, "
+ "\"vector_foo_byte\": [ 0, 1, 2, 3, 4, 5 ] }");
}
TEST_F(FlatbufferIntrospectionTest, StructTest) {
@@ -275,8 +276,8 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
EXPECT_EQ(out,
- "{\"foo_struct\": {\"foo_byte\": 5, \"nested_struct\": "
- "{\"foo_byte\": 10}}}");
+ "{ \"foo_struct\": { \"foo_byte\": 5, \"nested_struct\": "
+ "{ \"foo_byte\": 10 } } }");
}
TEST_F(FlatbufferIntrospectionTest, VectorStructTest) {
@@ -295,9 +296,9 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
EXPECT_EQ(out,
- "{\"vector_foo_struct\": [{\"foo_byte\": 5, \"nested_struct\": "
- "{\"foo_byte\": 1}}, {\"foo_byte\": 10, \"nested_struct\": "
- "{\"foo_byte\": 1}}]}");
+ "{ \"vector_foo_struct\": [ { \"foo_byte\": 5, \"nested_struct\": "
+ "{ \"foo_byte\": 1 } }, { \"foo_byte\": 10, \"nested_struct\": "
+ "{ \"foo_byte\": 1 } } ] }");
}
TEST_F(FlatbufferIntrospectionTest, VectorEnumTest) {
@@ -313,7 +314,7 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out, "{\"vector_foo_enum\": [\"UShort\", \"Obj\", \"UInt\"]}");
+ EXPECT_EQ(out, "{ \"vector_foo_enum\": [ \"UShort\", \"Obj\", \"UInt\" ] }");
}
TEST_F(FlatbufferIntrospectionTest, StructEnumTest) {
@@ -328,7 +329,7 @@
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out, "{\"foo_struct_enum\": {\"foo_enum\": \"UShort\"}}");
+ EXPECT_EQ(out, "{ \"foo_struct_enum\": { \"foo_enum\": \"UShort\" } }");
}
TEST_F(FlatbufferIntrospectionTest, StringEscapeTest) {
@@ -342,7 +343,7 @@
builder.Finish(config_builder.Finish());
std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
- EXPECT_EQ(out, "{\"foo_string\": \"\\\"\\\\\\b\\f\\n\\r\\t\"}");
+ EXPECT_EQ(out, "{ \"foo_string\": \"\\\"\\\\\\b\\f\\n\\r\\t\" }");
}
TEST_F(FlatbufferIntrospectionTest, TrimmedVector) {
@@ -362,7 +363,7 @@
std::string out =
FlatbufferToJson(schema_, builder.GetBufferPointer(),
{.multi_line = false, .max_vector_size = 100});
- EXPECT_EQ(out, "{\"vector_foo_int\": [ \"... 101 elements ...\" ]}");
+ EXPECT_EQ(out, "{ \"vector_foo_int\": [ \"... 101 elements ...\" ] }");
}
TEST_F(FlatbufferIntrospectionTest, MultilineTest) {
@@ -402,7 +403,7 @@
"{\n"
" \"foo_struct\": {\n"
" \"foo_byte\": 5,\n"
- " \"nested_struct\": {\"foo_byte\": 10}\n"
+ " \"nested_struct\": { \"foo_byte\": 10 }\n"
" }\n"
"}");
}
@@ -428,11 +429,11 @@
" \"vector_foo_struct\": [\n"
" {\n"
" \"foo_byte\": 5,\n"
- " \"nested_struct\": {\"foo_byte\": 1}\n"
+ " \"nested_struct\": { \"foo_byte\": 1 }\n"
" },\n"
" {\n"
" \"foo_byte\": 10,\n"
- " \"nested_struct\": {\"foo_byte\": 1}\n"
+ " \"nested_struct\": { \"foo_byte\": 1 }\n"
" }\n"
" ]\n"
"}");
@@ -464,10 +465,10 @@
EXPECT_EQ(out,
"{\n"
- " \"vector_foo_double\": [0, 0.111111111111111, "
- "0.222222222222222, 0.333333333333333],\n"
- " \"vector_foo_float\": [0, 0.111111, 0.222222, 0.333333],\n"
- " \"vector_foo_int\": [-300, -200, -100, 0, 100, 200, 300]\n"
+ " \"vector_foo_double\": [ 0, 0.111111111111111, "
+ "0.222222222222222, 0.333333333333333 ],\n"
+ " \"vector_foo_float\": [ 0, 0.111111, 0.222222, 0.333333 ],\n"
+ " \"vector_foo_int\": [ -300, -200, -100, 0, 100, 200, 300 ]\n"
"}");
}
diff --git a/aos/flatbuffers/base.cc b/aos/flatbuffers/base.cc
index 697f837..8ad3b98 100644
--- a/aos/flatbuffers/base.cc
+++ b/aos/flatbuffers/base.cc
@@ -7,6 +7,31 @@
}
} // namespace
+ResizeableObject::ResizeableObject(ResizeableObject &&other)
+ : buffer_(other.buffer_),
+ parent_(other.parent_),
+ owned_allocator_(std::move(other.owned_allocator_)),
+ allocator_(other.allocator_) {
+ // At this stage in the move the move constructors of the inherited types have
+ // not yet been called, so we edit the state of the other object now so that
+ // when everything is moved over into the new objects they will have the
+ // correct pointers.
+ for (size_t index = 0; index < other.NumberOfSubObjects(); ++index) {
+ SubObject object = other.GetSubObject(index);
+ if (object.object != nullptr) {
+ object.object->parent_ = this;
+ }
+ }
+ other.buffer_ = {};
+ other.allocator_ = nullptr;
+ other.parent_ = nullptr;
+ // Sanity check that the std::unique_ptr move didn't reallocate/move memory
+ // around.
+ if (owned_allocator_.get() != nullptr) {
+ CHECK_EQ(owned_allocator_.get(), allocator_);
+ }
+}
+
bool ResizeableObject::InsertBytes(void *insertion_point, size_t bytes,
SetZero set_zero) {
// See comments on InsertBytes() declaration and in FixObjects()
diff --git a/aos/flatbuffers/base.h b/aos/flatbuffers/base.h
index f99dbb8..387dbc3 100644
--- a/aos/flatbuffers/base.h
+++ b/aos/flatbuffers/base.h
@@ -99,13 +99,7 @@
// Users do not end up using the move constructor; however, it is needed to
// handle the fact that a ResizeableObject may be a member of an std::vector
// in the various generated types.
- ResizeableObject(ResizeableObject &&other)
- : buffer_(other.buffer_),
- owned_allocator_(std::move(other.owned_allocator_)),
- allocator_(owned_allocator_.get()) {
- other.buffer_ = {};
- other.allocator_ = nullptr;
- }
+ ResizeableObject(ResizeableObject &&other);
// Required alignment of this object.
virtual size_t Alignment() const = 0;
// Offset from the start of buffer() to the actual start of the object in
diff --git a/aos/flatbuffers/static_flatbuffers_test.cc b/aos/flatbuffers/static_flatbuffers_test.cc
index 52fa01e..1846540 100644
--- a/aos/flatbuffers/static_flatbuffers_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_test.cc
@@ -1094,4 +1094,58 @@
VerifyJson<::aos::testing::UseSchemaStatic>("{\n\n}");
}
+// Tests that we can use the move constructor on a Builder.
+TEST_F(StaticFlatbuffersTest, BuilderMoveConstructor) {
+ uint8_t buffer[Builder<TestTableStatic>::kBufferSize];
+ aos::fbs::SpanAllocator allocator({buffer, sizeof(buffer)});
+ Builder<TestTableStatic> builder_from(&allocator);
+ Builder<TestTableStatic> builder(std::move(builder_from));
+ TestTableStatic *object = builder.get();
+ object->set_scalar(123);
+ {
+ auto vector = object->add_vector_of_scalars();
+ ASSERT_TRUE(vector->emplace_back(4));
+ ASSERT_TRUE(vector->emplace_back(5));
+ }
+ {
+ auto string = object->add_string();
+ string->SetString("Hello, World!");
+ }
+ {
+ auto vector_of_strings = object->add_vector_of_strings();
+ auto sub_string = CHECK_NOTNULL(vector_of_strings->emplace_back());
+ ASSERT_TRUE(sub_string->emplace_back('D'));
+ }
+ { object->set_substruct({971, 254}); }
+ {
+ auto subtable = object->add_subtable();
+ subtable->set_foo(1234);
+ }
+ {
+ auto vector = object->add_vector_of_structs();
+ ASSERT_TRUE(vector->emplace_back({48, 67}));
+ ASSERT_TRUE(vector->emplace_back({118, 148}));
+ ASSERT_TRUE(vector->emplace_back({971, 973}));
+ // Max vector size is three; this should fail.
+ ASSERT_FALSE(vector->emplace_back({1114, 2056}));
+ // We don't have any extra space available.
+ ASSERT_FALSE(vector->reserve(4));
+ ASSERT_FALSE(vector->emplace_back({1114, 2056}));
+ }
+ {
+ auto vector = object->add_vector_of_tables();
+ auto subobject = vector->emplace_back();
+ subobject->set_foo(222);
+ }
+ {
+ auto subtable = object->add_included_table();
+ subtable->set_foo(included::TestEnum::B);
+ }
+ ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
+ VLOG(1) << aos::FlatbufferToJson(builder.AsFlatbufferSpan(),
+ {.multi_line = true});
+ VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
+ TestMemory(builder.buffer());
+}
+
} // namespace aos::fbs::testing
diff --git a/aos/flatbuffers/static_vector.h b/aos/flatbuffers/static_vector.h
index 6133075..4ce49a7 100644
--- a/aos/flatbuffers/static_vector.h
+++ b/aos/flatbuffers/static_vector.h
@@ -291,6 +291,9 @@
// if the allocation failed for some reason.
// Note that reductions in size will not currently result in the allocated
// size actually changing.
+ // For vectors of non-inline types (e.g., vectors of strings or vectors of
+ // tables), reserve() will allocate memory in an internal vector that we use
+ // for storing some metadata.
[[nodiscard]] bool reserve(size_t new_length) {
if (new_length > allocated_length_) {
const size_t new_elements = new_length - allocated_length_;
diff --git a/aos/ipc_lib/lockless_queue.cc b/aos/ipc_lib/lockless_queue.cc
index 9d14d8f..57a2e9e 100644
--- a/aos/ipc_lib/lockless_queue.cc
+++ b/aos/ipc_lib/lockless_queue.cc
@@ -953,6 +953,7 @@
const char *data, size_t length,
monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid,
monotonic_clock::time_point *monotonic_sent_time,
realtime_clock::time_point *realtime_sent_time, uint32_t *queue_index) {
@@ -962,13 +963,15 @@
// adhere to this convention and place it at the end.
memcpy((reinterpret_cast<char *>(Data()) + size() - length), data, length);
return Send(length, monotonic_remote_time, realtime_remote_time,
- remote_queue_index, source_boot_uuid, monotonic_sent_time,
- realtime_sent_time, queue_index);
+ monotonic_remote_transmit_time, remote_queue_index,
+ source_boot_uuid, monotonic_sent_time, realtime_sent_time,
+ queue_index);
}
LocklessQueueSender::Result LocklessQueueSender::Send(
size_t length, monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid,
monotonic_clock::time_point *monotonic_sent_time,
realtime_clock::time_point *realtime_sent_time, uint32_t *queue_index) {
@@ -997,6 +1000,8 @@
message->header.source_boot_uuid = source_boot_uuid;
message->header.monotonic_remote_time = monotonic_remote_time;
message->header.realtime_remote_time = realtime_remote_time;
+ message->header.monotonic_remote_transmit_time =
+ monotonic_remote_transmit_time;
Index to_replace = Index::Invalid();
while (true) {
@@ -1298,6 +1303,7 @@
monotonic_clock::time_point *monotonic_sent_time,
realtime_clock::time_point *realtime_sent_time,
monotonic_clock::time_point *monotonic_remote_time,
+ monotonic_clock::time_point *monotonic_remote_transmit_time,
realtime_clock::time_point *realtime_remote_time,
uint32_t *remote_queue_index, UUID *source_boot_uuid, size_t *length,
char *data,
@@ -1379,6 +1385,8 @@
context.monotonic_event_time = m->header.monotonic_sent_time;
context.realtime_event_time = m->header.realtime_sent_time;
context.monotonic_remote_time = m->header.monotonic_remote_time;
+ context.monotonic_remote_transmit_time =
+ m->header.monotonic_remote_transmit_time;
context.realtime_remote_time = m->header.realtime_remote_time;
context.queue_index = queue_index.index();
if (m->header.remote_queue_index == 0xffffffffu) {
@@ -1443,6 +1451,7 @@
*realtime_sent_time = context.realtime_event_time;
*remote_queue_index = context.remote_queue_index;
*monotonic_remote_time = context.monotonic_remote_time;
+ *monotonic_remote_transmit_time = context.monotonic_remote_transmit_time;
*realtime_remote_time = context.realtime_remote_time;
*source_boot_uuid = context.source_boot_uuid;
*length = context.size;
@@ -1597,6 +1606,12 @@
<< m->header.monotonic_remote_time << " 0x" << std::hex
<< m->header.monotonic_remote_time.time_since_epoch().count()
<< std::dec << ::std::endl;
+ ::std::cout
+ << " monotonic_clock::time_point "
+ "monotonic_remote_transmit_time = "
+ << m->header.monotonic_remote_transmit_time << " 0x" << std::hex
+ << m->header.monotonic_remote_transmit_time.time_since_epoch().count()
+ << std::dec << ::std::endl;
::std::cout << " realtime_clock::time_point realtime_remote_time = "
<< m->header.realtime_remote_time << " 0x" << std::hex
<< m->header.realtime_remote_time.time_since_epoch().count()
diff --git a/aos/ipc_lib/lockless_queue.h b/aos/ipc_lib/lockless_queue.h
index 4f867b9..462b2b6 100644
--- a/aos/ipc_lib/lockless_queue.h
+++ b/aos/ipc_lib/lockless_queue.h
@@ -91,6 +91,7 @@
// passed through.
monotonic_clock::time_point monotonic_remote_time;
realtime_clock::time_point realtime_remote_time;
+ monotonic_clock::time_point monotonic_remote_transmit_time;
// Queue index from the remote node.
uint32_t remote_queue_index;
@@ -326,6 +327,7 @@
LocklessQueueSender::Result Send(
size_t length, monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid,
monotonic_clock::time_point *monotonic_sent_time = nullptr,
realtime_clock::time_point *realtime_sent_time = nullptr,
@@ -336,6 +338,7 @@
const char *data, size_t length,
monotonic_clock::time_point monotonic_remote_time,
realtime_clock::time_point realtime_remote_time,
+ monotonic_clock::time_point monotonic_remote_transmit_time,
uint32_t remote_queue_index, const UUID &source_boot_uuid,
monotonic_clock::time_point *monotonic_sent_time = nullptr,
realtime_clock::time_point *realtime_sent_time = nullptr,
@@ -442,6 +445,7 @@
uint32_t queue_index, monotonic_clock::time_point *monotonic_sent_time,
realtime_clock::time_point *realtime_sent_time,
monotonic_clock::time_point *monotonic_remote_time,
+ monotonic_clock::time_point *monotonic_remote_transmit_time,
realtime_clock::time_point *realtime_remote_time,
uint32_t *remote_queue_index, UUID *source_boot_uuid, size_t *length,
char *data,
diff --git a/aos/ipc_lib/lockless_queue_death_test.cc b/aos/ipc_lib/lockless_queue_death_test.cc
index ccf95f7..bc5ebb7 100644
--- a/aos/ipc_lib/lockless_queue_death_test.cc
+++ b/aos/ipc_lib/lockless_queue_death_test.cc
@@ -79,10 +79,11 @@
for (int i = 0; i < 5; ++i) {
char data[100];
size_t s = snprintf(data, sizeof(data), "foobar%d", i + 1);
- ASSERT_EQ(sender.Send(data, s + 1, monotonic_clock::min_time,
- realtime_clock::min_time, 0xffffffffl,
- UUID::Zero(), nullptr, nullptr, nullptr),
- LocklessQueueSender::Result::GOOD);
+ ASSERT_EQ(
+ sender.Send(data, s + 1, monotonic_clock::min_time,
+ realtime_clock::min_time, monotonic_clock::min_time,
+ 0xffffffffl, UUID::Zero(), nullptr, nullptr, nullptr),
+ LocklessQueueSender::Result::GOOD);
// Pin a message, so when we keep writing we will exercise the pinning
// logic.
if (i == 1) {
@@ -156,10 +157,11 @@
// Send a message to make sure that the queue still works.
char data[100];
size_t s = snprintf(data, sizeof(data), "foobar%d", 971);
- ASSERT_EQ(sender.Send(data, s + 1, monotonic_clock::min_time,
- realtime_clock::min_time, 0xffffffffl,
- UUID::Zero(), nullptr, nullptr, nullptr),
- LocklessQueueSender::Result::GOOD);
+ ASSERT_EQ(
+ sender.Send(data, s + 1, monotonic_clock::min_time,
+ realtime_clock::min_time, monotonic_clock::min_time,
+ 0xffffffffl, UUID::Zero(), nullptr, nullptr, nullptr),
+ LocklessQueueSender::Result::GOOD);
}
// Now loop through the queue and make sure the number in the snprintf
@@ -175,17 +177,18 @@
monotonic_clock::time_point monotonic_sent_time;
realtime_clock::time_point realtime_sent_time;
monotonic_clock::time_point monotonic_remote_time;
+ monotonic_clock::time_point monotonic_remote_transmit_time;
realtime_clock::time_point realtime_remote_time;
uint32_t remote_queue_index;
UUID source_boot_uuid;
char read_data[1024];
size_t length;
- LocklessQueueReader::Result read_result =
- reader.Read(i, &monotonic_sent_time, &realtime_sent_time,
- &monotonic_remote_time, &realtime_remote_time,
- &remote_queue_index, &source_boot_uuid, &length,
- &(read_data[0]), std::ref(should_read));
+ LocklessQueueReader::Result read_result = reader.Read(
+ i, &monotonic_sent_time, &realtime_sent_time,
+ &monotonic_remote_time, &monotonic_remote_transmit_time,
+ &realtime_remote_time, &remote_queue_index, &source_boot_uuid,
+ &length, &(read_data[0]), std::ref(should_read));
if (read_result != LocklessQueueReader::Result::GOOD) {
if (read_result == LocklessQueueReader::Result::TOO_OLD) {
diff --git a/aos/ipc_lib/lockless_queue_test.cc b/aos/ipc_lib/lockless_queue_test.cc
index bfd9916..a2c0992 100644
--- a/aos/ipc_lib/lockless_queue_test.cc
+++ b/aos/ipc_lib/lockless_queue_test.cc
@@ -251,8 +251,8 @@
char data[100];
size_t s = snprintf(data, sizeof(data), "foobar%d", i);
ASSERT_EQ(sender.Send(data, s, monotonic_clock::min_time,
- realtime_clock::min_time, 0xffffffffu, UUID::Zero(),
- nullptr, nullptr, nullptr),
+ realtime_clock::min_time, monotonic_clock::min_time,
+ 0xffffffffu, UUID::Zero(), nullptr, nullptr, nullptr),
LocklessQueueSender::Result::GOOD);
// Confirm that the queue index still makes sense. This is easier since the
@@ -263,6 +263,7 @@
monotonic_clock::time_point monotonic_sent_time;
realtime_clock::time_point realtime_sent_time;
monotonic_clock::time_point monotonic_remote_time;
+ monotonic_clock::time_point monotonic_remote_transmit_time;
realtime_clock::time_point realtime_remote_time;
uint32_t remote_queue_index;
UUID source_boot_uuid;
@@ -277,8 +278,9 @@
}
LocklessQueueReader::Result read_result = reader.Read(
index.index(), &monotonic_sent_time, &realtime_sent_time,
- &monotonic_remote_time, &realtime_remote_time, &remote_queue_index,
- &source_boot_uuid, &length, &(read_data[0]), std::ref(should_read));
+ &monotonic_remote_time, &monotonic_remote_transmit_time,
+ &realtime_remote_time, &remote_queue_index, &source_boot_uuid, &length,
+ &(read_data[0]), std::ref(should_read));
// This should either return GOOD, or TOO_OLD if it is before the start of
// the queue.
@@ -450,6 +452,7 @@
monotonic_clock::time_point monotonic_sent_time;
realtime_clock::time_point realtime_sent_time;
monotonic_clock::time_point monotonic_remote_time;
+ monotonic_clock::time_point monotonic_remote_transmit_time;
realtime_clock::time_point realtime_remote_time;
uint32_t remote_queue_index;
UUID source_boot_uuid;
@@ -458,8 +461,9 @@
LocklessQueueReader::Result read_result = reader.Read(
i, &monotonic_sent_time, &realtime_sent_time, &monotonic_remote_time,
- &realtime_remote_time, &remote_queue_index, &source_boot_uuid, &length,
- &(read_data[0]), should_read_callback);
+ &monotonic_remote_transmit_time, &realtime_remote_time,
+ &remote_queue_index, &source_boot_uuid, &length, &(read_data[0]),
+ should_read_callback);
if (read_result != LocklessQueueReader::Result::GOOD) {
if (read_result == LocklessQueueReader::Result::TOO_OLD) {
@@ -526,10 +530,11 @@
for (int i = 0; i < 5; ++i) {
char data[100];
size_t s = snprintf(data, sizeof(data), "foobar%d", i + 1);
- ASSERT_EQ(sender.Send(data, s + 1, monotonic_clock::min_time,
- realtime_clock::min_time, 0xffffffffl,
- UUID::Zero(), nullptr, nullptr, nullptr),
- LocklessQueueSender::Result::GOOD);
+ ASSERT_EQ(
+ sender.Send(data, s + 1, monotonic_clock::min_time,
+ realtime_clock::min_time, monotonic_clock::min_time,
+ 0xffffffffl, UUID::Zero(), nullptr, nullptr, nullptr),
+ LocklessQueueSender::Result::GOOD);
}
},
[config, &tid](void *raw_memory) {
@@ -549,10 +554,11 @@
{
char data[100];
size_t s = snprintf(data, sizeof(data), "foobar%d", i + 1);
- ASSERT_EQ(sender.Send(data, s + 1, monotonic_clock::min_time,
- realtime_clock::min_time, 0xffffffffl,
- UUID::Zero(), nullptr, nullptr, nullptr),
- LocklessQueueSender::Result::GOOD);
+ ASSERT_EQ(
+ sender.Send(data, s + 1, monotonic_clock::min_time,
+ realtime_clock::min_time, monotonic_clock::min_time,
+ 0xffffffffl, UUID::Zero(), nullptr, nullptr, nullptr),
+ LocklessQueueSender::Result::GOOD);
}
// Now, make sure we can send 1 message and receive it to confirm we
diff --git a/aos/ipc_lib/memory_mapped_queue.cc b/aos/ipc_lib/memory_mapped_queue.cc
index d73b850..dc457fe 100644
--- a/aos/ipc_lib/memory_mapped_queue.cc
+++ b/aos/ipc_lib/memory_mapped_queue.cc
@@ -16,15 +16,14 @@
std::string ShmPath(std::string_view shm_base, const Channel *channel) {
CHECK(channel->has_type());
- return ShmFolder(shm_base, channel) + channel->type()->str() + ".v6";
+ return ShmFolder(shm_base, channel) + channel->type()->str() + ".v7";
}
-void PageFaultDataWrite(char *data, size_t size) {
+void PageFaultDataWrite(char *data, size_t size, const long page_size) {
// This just has to divide the actual page size. Being smaller will make this
// a bit slower than necessary, but not much. 1024 is a pretty conservative
// choice (most pages are probably 4096).
- static constexpr size_t kPageSize = 1024;
- const size_t pages = (size + kPageSize - 1) / kPageSize;
+ const size_t pages = (size + page_size - 1) / page_size;
for (size_t i = 0; i < pages; ++i) {
char zero = 0;
// We need to ensure there's a writable pagetable entry, but avoid modifying
@@ -39,20 +38,16 @@
//
// This is the simplest operation I could think of which achieves that:
// "store 0 if it's already 0".
- __atomic_compare_exchange_n(&data[i * kPageSize], &zero, 0, true,
+ __atomic_compare_exchange_n(&data[i * page_size], &zero, 0, true,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
}
-void PageFaultDataRead(const char *data, size_t size) {
- // This just has to divide the actual page size. Being smaller will make this
- // a bit slower than necessary, but not much. 1024 is a pretty conservative
- // choice (most pages are probably 4096).
- static constexpr size_t kPageSize = 1024;
- const size_t pages = (size + kPageSize - 1) / kPageSize;
+void PageFaultDataRead(const char *data, size_t size, const long page_size) {
+ const size_t pages = (size + page_size - 1) / page_size;
for (size_t i = 0; i < pages; ++i) {
// We need to ensure there's a readable pagetable entry.
- __atomic_load_n(&data[i * kPageSize], __ATOMIC_RELAXED);
+ __atomic_load_n(&data[i * page_size], __ATOMIC_RELAXED);
}
}
@@ -85,6 +80,7 @@
const Configuration *config,
const Channel *channel)
: config_(MakeQueueConfiguration(config, channel)) {
+ const long kSystemPageSize = sysconf(_SC_PAGESIZE);
std::string path = ShmPath(shm_base, channel);
size_ = ipc_lib::LocklessQueueMemorySize(config_);
@@ -128,8 +124,9 @@
const_data_ = mmap(NULL, size_, PROT_READ, MAP_SHARED, fd, 0);
PCHECK(const_data_ != MAP_FAILED);
PCHECK(close(fd) == 0);
- PageFaultDataWrite(static_cast<char *>(data_), size_);
- PageFaultDataRead(static_cast<const char *>(const_data_), size_);
+ PageFaultDataWrite(static_cast<char *>(data_), size_, kSystemPageSize);
+ PageFaultDataRead(static_cast<const char *>(const_data_), size_,
+ kSystemPageSize);
ipc_lib::InitializeLocklessQueueMemory(memory(), config_);
}
diff --git a/aos/ipc_lib/queue_racer.cc b/aos/ipc_lib/queue_racer.cc
index aa73f2b..2797c24 100644
--- a/aos/ipc_lib/queue_racer.cc
+++ b/aos/ipc_lib/queue_racer.cc
@@ -197,7 +197,8 @@
++started_writes_;
auto result =
sender.Send(sizeof(ThreadPlusCount), aos::monotonic_clock::min_time,
- aos::realtime_clock::min_time, 0xffffffff,
+ aos::realtime_clock::min_time,
+ aos::monotonic_clock::min_time, 0xffffffff,
UUID::FromSpan(absl::Span<const uint8_t>(
reinterpret_cast<const uint8_t *>(&tpc),
sizeof(ThreadPlusCount))),
@@ -309,6 +310,7 @@
monotonic_clock::time_point monotonic_sent_time;
realtime_clock::time_point realtime_sent_time;
monotonic_clock::time_point monotonic_remote_time;
+ monotonic_clock::time_point monotonic_remote_transmit_time;
realtime_clock::time_point realtime_remote_time;
UUID source_boot_uuid;
uint32_t remote_queue_index;
@@ -321,14 +323,16 @@
0xffffffffu, LocklessQueueSize(queue_.memory())));
LocklessQueueReader::Result read_result =
set_should_read
- ? reader.Read(wrapped_i, &monotonic_sent_time, &realtime_sent_time,
- &monotonic_remote_time, &realtime_remote_time,
- &remote_queue_index, &source_boot_uuid, &length,
- &(read_data[0]), std::ref(should_read))
+ ? reader.Read(
+ wrapped_i, &monotonic_sent_time, &realtime_sent_time,
+ &monotonic_remote_time, &monotonic_remote_transmit_time,
+ &realtime_remote_time, &remote_queue_index, &source_boot_uuid,
+ &length, &(read_data[0]), std::ref(should_read))
: reader.Read(wrapped_i, &monotonic_sent_time, &realtime_sent_time,
- &monotonic_remote_time, &realtime_remote_time,
- &remote_queue_index, &source_boot_uuid, &length,
- &(read_data[0]), nop);
+ &monotonic_remote_time,
+ &monotonic_remote_transmit_time,
+ &realtime_remote_time, &remote_queue_index,
+ &source_boot_uuid, &length, &(read_data[0]), nop);
// The code in lockless_queue.cc reads everything but data, checks that the
// header hasn't changed, then reads the data. So, if we succeed and both
diff --git a/aos/json_to_flatbuffer_test.cc b/aos/json_to_flatbuffer_test.cc
index 85e21d4..7c69acd 100644
--- a/aos/json_to_flatbuffer_test.cc
+++ b/aos/json_to_flatbuffer_test.cc
@@ -11,6 +11,8 @@
class JsonToFlatbufferTest : public ::testing::Test {
public:
+ enum class TestReflection { kYes, kNo };
+
JsonToFlatbufferTest() {}
FlatbufferVector<reflection::Schema> Schema() {
@@ -18,14 +20,23 @@
ArtifactPath("aos/json_to_flatbuffer.bfbs"));
}
- bool JsonAndBack(const ::std::string str) { return JsonAndBack(str, str); }
+ // JsonAndBack tests using both the reflection::Schema* as well as the
+ // minireflect tables for both parsing and outputting JSON. However, there are
+ // currently minor discrepencies between how the JSON output works for the two
+ // modes, so some tests must manually disable testing of the
+ // FlatbufferToJson() overload that takes a reflection::Schema*.
+ bool JsonAndBack(const char *str, TestReflection test_reflection_to_json =
+ TestReflection::kYes) {
+ return JsonAndBack(str, str, test_reflection_to_json);
+ }
- bool JsonAndBack(const ::std::string in, const ::std::string out) {
- printf("Testing: %s\n", in.c_str());
+ bool JsonAndBack(
+ const char *in, const char *out,
+ TestReflection test_reflection_to_json = TestReflection::kYes) {
FlatbufferDetachedBuffer<Configuration> fb_typetable =
- JsonToFlatbuffer<Configuration>(in.data());
+ JsonToFlatbuffer<Configuration>(in);
FlatbufferDetachedBuffer<Configuration> fb_reflection =
- JsonToFlatbuffer(in.data(), FlatbufferType(&Schema().message()));
+ JsonToFlatbuffer(in, FlatbufferType(&Schema().message()));
if (fb_typetable.span().size() == 0) {
return false;
@@ -36,13 +47,24 @@
const ::std::string back_typetable = FlatbufferToJson(fb_typetable);
const ::std::string back_reflection = FlatbufferToJson(fb_reflection);
+ const ::std::string back_reflection_reflection =
+ FlatbufferToJson(&Schema().message(), fb_reflection.span().data());
- printf("Back to string via TypeTable: %s\n", back_typetable.c_str());
- printf("Back to string via reflection: %s\n", back_reflection.c_str());
+ printf("Back to table via TypeTable and to string via TypeTable: %s\n",
+ back_typetable.c_str());
+ printf("Back to table via reflection and to string via TypeTable: %s\n",
+ back_reflection.c_str());
+ if (test_reflection_to_json == TestReflection::kYes) {
+ printf("Back to table via reflection and to string via reflection: %s\n",
+ back_reflection_reflection.c_str());
+ }
- const bool as_expected = back_typetable == out && back_reflection == out;
+ const bool as_expected =
+ back_typetable == out && back_reflection == out &&
+ ((test_reflection_to_json == TestReflection::kNo) ||
+ (back_reflection_reflection == out));
if (!as_expected) {
- printf("But expected: %s\n", out.c_str());
+ printf("But expected: %s\n", out);
}
return as_expected;
}
@@ -71,8 +93,10 @@
EXPECT_TRUE(JsonAndBack("{ \"foo_long\": 5 }"));
EXPECT_TRUE(JsonAndBack("{ \"foo_ulong\": 5 }"));
- EXPECT_TRUE(JsonAndBack("{ \"foo_float\": 5.0 }"));
- EXPECT_TRUE(JsonAndBack("{ \"foo_double\": 5.0 }"));
+ // TODO(james): Make FlatbufferToJson() always print out integer
+ // floating-point numbers identically.
+ EXPECT_TRUE(JsonAndBack("{ \"foo_float\": 5.0 }", TestReflection::kNo));
+ EXPECT_TRUE(JsonAndBack("{ \"foo_double\": 5.0 }", TestReflection::kNo));
EXPECT_TRUE(JsonAndBack("{ \"foo_enum\": \"None\" }"));
EXPECT_TRUE(JsonAndBack("{ \"foo_enum\": \"UType\" }"));
@@ -93,7 +117,8 @@
EXPECT_TRUE(JsonAndBack(
"{ \"foo_struct_scalars\": { \"foo_float\": 1.234, \"foo_double\": "
"4.567, \"foo_int32\": -971, \"foo_uint32\": 4294967294, \"foo_int64\": "
- "-1030, \"foo_uint64\": 18446744073709551614 } }"));
+ "-1030, \"foo_uint64\": 18446744073709551614 } }",
+ TestReflection::kNo));
// Confirm that we parse integers into floating point fields correctly.
EXPECT_TRUE(JsonAndBack(
"{ \"foo_struct_scalars\": { \"foo_float\": 1, \"foo_double\": "
@@ -101,13 +126,15 @@
"5, \"foo_uint64\": 6 } }",
"{ \"foo_struct_scalars\": { \"foo_float\": 1.0, \"foo_double\": "
"2.0, \"foo_int32\": 3, \"foo_uint32\": 4, \"foo_int64\": "
- "5, \"foo_uint64\": 6 } }"));
+ "5, \"foo_uint64\": 6 } }",
+ TestReflection::kNo));
EXPECT_TRUE(JsonAndBack(
"{ \"vector_foo_struct_scalars\": [ { \"foo_float\": 1.234, "
"\"foo_double\": 4.567, \"foo_int32\": -971, \"foo_uint32\": 4294967294, "
"\"foo_int64\": -1030, \"foo_uint64\": 18446744073709551614 }, { "
"\"foo_float\": 2.0, \"foo_double\": 4.1, \"foo_int32\": 10, "
- "\"foo_uint32\": 13, \"foo_int64\": 15, \"foo_uint64\": 18 } ] }"));
+ "\"foo_uint32\": 13, \"foo_int64\": 15, \"foo_uint64\": 18 } ] }",
+ TestReflection::kNo));
EXPECT_TRUE(
JsonAndBack("{ \"foo_struct_enum\": { \"foo_enum\": \"UByte\" } }"));
EXPECT_TRUE(
@@ -160,14 +187,24 @@
// Tests that unicode is handled correctly
TEST_F(JsonToFlatbufferTest, Unicode) {
- EXPECT_TRUE(JsonAndBack("{ \"foo_string\": \"\\uF672\" }"));
- EXPECT_TRUE(JsonAndBack("{ \"foo_string\": \"\\uEFEF\" }"));
- EXPECT_TRUE(JsonAndBack("{ \"foo_string\": \"helloworld\\uD83E\\uDE94\" }"));
- EXPECT_TRUE(JsonAndBack("{ \"foo_string\": \"\\uD83C\\uDF32\" }"));
- EXPECT_FALSE(JsonAndBack("{ \"foo_string\": \"\\uP890\" }"));
- EXPECT_FALSE(JsonAndBack("{ \"foo_string\": \"\\u!FA8\" }"));
- EXPECT_FALSE(JsonAndBack("{ \"foo_string\": \"\\uF89\" }"));
- EXPECT_FALSE(JsonAndBack("{ \"foo_string\": \"\\uD83C\" }"));
+ // The reflection-based FlatbufferToJson outputs actual unicode rather than
+ // escaped code-points.
+ EXPECT_TRUE(
+ JsonAndBack("{ \"foo_string\": \"\\uF672\" }", TestReflection::kNo));
+ EXPECT_TRUE(
+ JsonAndBack("{ \"foo_string\": \"\\uEFEF\" }", TestReflection::kNo));
+ EXPECT_TRUE(JsonAndBack("{ \"foo_string\": \"helloworld\\uD83E\\uDE94\" }",
+ TestReflection::kNo));
+ EXPECT_TRUE(JsonAndBack("{ \"foo_string\": \"\\uD83C\\uDF32\" }",
+ TestReflection::kNo));
+ EXPECT_FALSE(
+ JsonAndBack("{ \"foo_string\": \"\\uP890\" }", TestReflection::kNo));
+ EXPECT_FALSE(
+ JsonAndBack("{ \"foo_string\": \"\\u!FA8\" }", TestReflection::kNo));
+ EXPECT_FALSE(
+ JsonAndBack("{ \"foo_string\": \"\\uF89\" }", TestReflection::kNo));
+ EXPECT_FALSE(
+ JsonAndBack("{ \"foo_string\": \"\\uD83C\" }", TestReflection::kNo));
}
// Tests that we can handle decimal points.
@@ -246,15 +283,19 @@
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_ulong\": [ 9, 7, 1 ] }"));
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_ulong\": [ ] }"));
- EXPECT_TRUE(JsonAndBack("{ \"vector_foo_float\": [ 9.0, 7.0, 1.0 ] }"));
+ EXPECT_TRUE(JsonAndBack("{ \"vector_foo_float\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_float\": [ ] }"));
- EXPECT_TRUE(JsonAndBack("{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+ EXPECT_TRUE(JsonAndBack("{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_double\": [ ] }"));
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_float\": [ 9, 7, 1 ] }",
- "{ \"vector_foo_float\": [ 9.0, 7.0, 1.0 ] }"));
+ "{ \"vector_foo_float\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_double\": [ 9, 7, 1 ] }",
- "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+ "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_string\": [ \"bar\", \"baz\" ] }"));
EXPECT_TRUE(JsonAndBack("{ \"vector_foo_string\": [ ] }"));
@@ -295,7 +336,8 @@
/* foo */
"vector_foo_double": [ 9, 7, 1 ] /* foo */
} /* foo */)",
- "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+ "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
}
// Tests that C++ style comments get stripped.
@@ -304,7 +346,38 @@
// foo
"vector_foo_double": [ 9, 7, 1 ] // foo
} // foo)",
- "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+ "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
+
+ // Test empty comment on its own line doesn't remove the next line.
+ EXPECT_TRUE(JsonAndBack(R"({
+ //
+ "vector_foo_double": [ 9, 7, 1 ], // foo
+ "vector_foo_float": [ 3, 1, 4 ]
+} // foo)",
+ "{ \"vector_foo_float\": [ 3.0, 1.0, 4.0 ], "
+ "\"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
+
+ // Test empty comment at end of line doesn't remove the next line.
+ EXPECT_TRUE(JsonAndBack(R"({
+ // foo
+ "vector_foo_double": [ 2, 7, 1 ], //
+ "vector_foo_float": [ 3, 1, 4 ]
+} // foo)",
+ "{ \"vector_foo_float\": [ 3.0, 1.0, 4.0 ], "
+ "\"vector_foo_double\": [ 2.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
+
+ // Test empty comment at end of document doesn't cause error.
+ EXPECT_TRUE(JsonAndBack(R"({
+ // foo
+ "vector_foo_double": [ 5, 6, 7 ], // foo
+ "vector_foo_float": [ 7, 8, 9 ]
+} //)",
+ "{ \"vector_foo_float\": [ 7.0, 8.0, 9.0 ], "
+ "\"vector_foo_double\": [ 5.0, 6.0, 7.0 ] }",
+ TestReflection::kNo));
}
// Tests that mixed style comments get stripped.
@@ -316,7 +389,8 @@
}
// foo
/* foo */)",
- "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+ "{ \"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
}
// Tests that multiple arrays get properly handled.
@@ -325,7 +399,8 @@
JsonAndBack("{ \"vector_foo_float\": [ 9, 7, 1 ], \"vector_foo_double\": "
"[ 9, 7, 1 ] }",
"{ \"vector_foo_float\": [ 9.0, 7.0, 1.0 ], "
- "\"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }"));
+ "\"vector_foo_double\": [ 9.0, 7.0, 1.0 ] }",
+ TestReflection::kNo));
}
// Tests that multiple arrays get properly handled.
diff --git a/aos/json_tokenizer.cc b/aos/json_tokenizer.cc
index d277c1e..32c9247 100644
--- a/aos/json_tokenizer.cc
+++ b/aos/json_tokenizer.cc
@@ -27,14 +27,17 @@
// C++ style comment. Keep consuming chars until newline, or until the
// end of the file if this is the last line (no newline at end of file).
while (true) {
- ConsumeChar();
+ // First check if we are at the end of the file.
if (AtEnd()) {
return;
}
+ // Then check if we are at the end of the line.
if (Char() == '\n') {
++linenumber_;
break;
}
+ // Advance to next character and repeat.
+ ConsumeChar();
}
} else {
// There is no fail. Once we are out of whitespace (including 0 of it),
diff --git a/aos/network/BUILD b/aos/network/BUILD
index db62286..8186abb 100644
--- a/aos/network/BUILD
+++ b/aos/network/BUILD
@@ -500,6 +500,32 @@
)
aos_config(
+ name = "message_bridge_test_mismatched_configs_pi1_and_pi2_config",
+ src = "message_bridge_test_mismatched_configs_pi1_and_pi2.json",
+ flatbuffers = [
+ ":remote_message_fbs",
+ "//aos/network:message_bridge_client_fbs",
+ "//aos/network:message_bridge_server_fbs",
+ "//aos/network:timestamp_fbs",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = ["//aos/events:aos_config"],
+)
+
+aos_config(
+ name = "message_bridge_test_mismatched_configs_pi1_and_pi3_config",
+ src = "message_bridge_test_mismatched_configs_pi1_and_pi3.json",
+ flatbuffers = [
+ ":remote_message_fbs",
+ "//aos/network:message_bridge_client_fbs",
+ "//aos/network:message_bridge_server_fbs",
+ "//aos/network:timestamp_fbs",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = ["//aos/events:aos_config"],
+)
+
+aos_config(
name = "message_bridge_auth_test_config",
src = "message_bridge_auth_test.json",
flatbuffers = [
@@ -594,6 +620,8 @@
data = [
":message_bridge_test_combined_timestamps_common_config",
":message_bridge_test_common_config",
+ ":message_bridge_test_mismatched_configs_pi1_and_pi2_config",
+ ":message_bridge_test_mismatched_configs_pi1_and_pi3_config",
],
flaky = True,
shard_count = 16,
@@ -613,6 +641,23 @@
],
)
+cc_test(
+ name = "message_bridge_server_status_test",
+ srcs = [
+ "message_bridge_server_status_test.cc",
+ ],
+ data = [
+ ":message_bridge_test_combined_timestamps_common_config",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":message_bridge_server_status",
+ "//aos/events:simulated_event_loop",
+ "//aos/testing:googletest",
+ "//aos/testing:path",
+ ],
+)
+
flatbuffer_cc_library(
name = "web_proxy_fbs",
srcs = ["web_proxy.fbs"],
diff --git a/aos/network/message_bridge_auth_client_lib.cc b/aos/network/message_bridge_auth_client_lib.cc
index 9430489..c277b4f 100644
--- a/aos/network/message_bridge_auth_client_lib.cc
+++ b/aos/network/message_bridge_auth_client_lib.cc
@@ -42,6 +42,7 @@
config_request_fetcher_(
event_loop_->MakeFetcher<SctpConfigRequest>("/aos")),
client_(SctpConfigService::NewStub(channel)) {
+ poll_timer_->set_name("poll");
event_loop_->OnRun([this] {
poll_timer_->Schedule(event_loop_->monotonic_now(),
std::chrono::milliseconds(1000));
@@ -69,7 +70,7 @@
Status status = client_->GetActiveKey(&context, request, &response);
if (!status.ok()) {
LOG_EVERY_N(ERROR, 50)
- << "Unable to retreive active SCTP authentication key from server";
+ << "Unable to retrieve active SCTP authentication key from server";
return {};
}
diff --git a/aos/network/message_bridge_client_lib.cc b/aos/network/message_bridge_client_lib.cc
index 620a069..90c0cc0 100644
--- a/aos/network/message_bridge_client_lib.cc
+++ b/aos/network/message_bridge_client_lib.cc
@@ -97,6 +97,7 @@
message_header_builder.add_queue_index(0);
message_header_builder.add_monotonic_remote_time(0);
message_header_builder.add_realtime_remote_time(0);
+ message_header_builder.add_monotonic_remote_transmit_time(0);
message_header_builder.add_remote_queue_index(0);
fbb.Finish(message_header_builder.Finish());
@@ -323,12 +324,14 @@
chrono::nanoseconds(remote_data->monotonic_sent_time())),
realtime_clock::time_point(
chrono::nanoseconds(remote_data->realtime_sent_time())),
+ monotonic_clock::time_point(
+ chrono::nanoseconds(remote_data->monotonic_remote_transmit_time())),
remote_data->queue_index(), remote_boot_uuid));
client_status_->SampleFilter(
client_index_,
monotonic_clock::time_point(
- chrono::nanoseconds(remote_data->monotonic_sent_time())),
+ chrono::nanoseconds(remote_data->monotonic_remote_transmit_time())),
sender->monotonic_sent_time(), remote_boot_uuid);
if (stream_reply_with_timestamp_[stream]) {
@@ -339,6 +342,8 @@
.queue_index = remote_data->queue_index(),
.monotonic_remote_time =
sender->monotonic_sent_time().time_since_epoch().count(),
+ .monotonic_remote_transmit_time =
+ remote_data->monotonic_remote_transmit_time(),
.realtime_remote_time =
sender->realtime_sent_time().time_since_epoch().count(),
.remote_queue_index = sender->sent_queue_index(),
@@ -402,6 +407,9 @@
timestamp.channel_index);
message_reception_reply_.mutable_message()->mutate_monotonic_sent_time(
timestamp.monotonic_sent_time);
+ message_reception_reply_.mutable_message()
+ ->mutate_monotonic_remote_transmit_time(
+ timestamp.monotonic_remote_transmit_time);
message_reception_reply_.mutable_message()->mutate_realtime_sent_time(
timestamp.realtime_sent_time);
message_reception_reply_.mutable_message()->mutate_queue_index(
diff --git a/aos/network/message_bridge_client_lib.h b/aos/network/message_bridge_client_lib.h
index 56d2563..97d5946 100644
--- a/aos/network/message_bridge_client_lib.h
+++ b/aos/network/message_bridge_client_lib.h
@@ -57,6 +57,7 @@
int64_t realtime_sent_time;
uint32_t queue_index;
int64_t monotonic_remote_time;
+ int64_t monotonic_remote_transmit_time;
int64_t realtime_remote_time;
uint32_t remote_queue_index;
};
diff --git a/aos/network/message_bridge_retry_test.cc b/aos/network/message_bridge_retry_test.cc
index 8229c99..e90bcb0 100644
--- a/aos/network/message_bridge_retry_test.cc
+++ b/aos/network/message_bridge_retry_test.cc
@@ -38,25 +38,25 @@
TEST_P(MessageBridgeParameterizedTest, ReliableRetries) {
// Set an absurdly small wmem max. This will help to trigger retries.
FLAGS_force_wmem_max = 1024;
- OnPi1();
+ pi1_.OnPi();
FLAGS_application_name = "sender";
- aos::ShmEventLoop send_event_loop(&config.message());
+ aos::ShmEventLoop send_event_loop(&config_.message());
aos::Sender<examples::Ping> ping_sender =
send_event_loop.MakeSender<examples::Ping>("/test");
SendPing(&ping_sender, 1);
aos::Fetcher<ServerStatistics> pi1_server_statistics_fetcher =
send_event_loop.MakeFetcher<ServerStatistics>("/aos");
- MakePi1Server();
- MakePi1Client();
+ pi1_.MakeServer();
+ pi1_.MakeClient();
// Now do it for "raspberrypi2", the client.
- OnPi2();
+ pi2_.OnPi();
- MakePi2Server();
+ pi2_.MakeServer();
- aos::ShmEventLoop receive_event_loop(&config.message());
+ aos::ShmEventLoop receive_event_loop(&config_.message());
aos::Fetcher<examples::Ping> ping_fetcher =
receive_event_loop.MakeFetcher<examples::Ping>("/test");
aos::Fetcher<ClientStatistics> pi2_client_statistics_fetcher =
@@ -66,15 +66,15 @@
EXPECT_FALSE(ping_fetcher.Fetch());
// Spin up the persistent pieces.
- StartPi1Server();
- StartPi1Client();
- StartPi2Server();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartServer();
{
constexpr size_t kNumPingMessages = 25;
// Now, spin up a client for 2 seconds.
- MakePi2Client();
- StartPi2Client();
+ pi2_.MakeClient();
+ pi2_.StartClient();
std::this_thread::sleep_for(std::chrono::seconds(2));
@@ -85,7 +85,7 @@
// Give plenty of time for retries to succeed.
std::this_thread::sleep_for(std::chrono::seconds(5));
- StopPi2Client();
+ pi2_.StopClient();
// Confirm there is no detected duplicate packet.
EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
@@ -124,9 +124,9 @@
}
// Shut everyone else down.
- StopPi1Client();
- StopPi2Server();
- StopPi1Server();
+ pi1_.StopClient();
+ pi2_.StopServer();
+ pi1_.StopServer();
}
INSTANTIATE_TEST_SUITE_P(MessageBridgeTests, MessageBridgeParameterizedTest,
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index a2df830..3c7a8d2 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -95,6 +95,8 @@
context.realtime_event_time.time_since_epoch().count());
remote_data_builder.add_data(data_offset);
remote_data_builder.add_boot_uuid(boot_uuid_offset);
+ remote_data_builder.add_monotonic_remote_transmit_time(
+ monotonic_clock::now().time_since_epoch().count());
// TODO(austin): Use an iovec to build it up in 3 parts to avoid the copy?
// Only useful when not logging.
@@ -304,6 +306,8 @@
remote_message_builder.add_remote_queue_index(
message_header->queue_index());
remote_message_builder.add_boot_uuid(boot_uuid_offset);
+ remote_message_builder.add_monotonic_remote_transmit_time(
+ message_header->monotonic_remote_transmit_time());
server_status->AddPartialDeliveries(peer.node_index,
partial_deliveries);
@@ -418,7 +422,10 @@
timestamp_loggers_(event_loop_),
server_(max_channels() + kControlStreams(), "",
event_loop->node()->port(), requested_authentication),
- server_status_(event_loop, [this]() { timestamp_state_->SendData(); }),
+ server_status_(event_loop,
+ [this](uint32_t, monotonic_clock::time_point) {
+ timestamp_state_->SendData();
+ }),
config_sha256_(std::move(config_sha256)),
allocator_(0),
refresh_key_timer_(event_loop->AddTimer([this]() { RequestAuthKey(); })),
@@ -672,44 +679,13 @@
HandleData(message.get());
break;
case Message::kOverflow:
- MaybeIncrementInvalidConnectionCount(nullptr);
+ server_status_.MaybeIncrementInvalidConnectionCount(nullptr);
NodeDisconnected(message->header.rcvinfo.rcv_assoc_id);
break;
}
server_.FreeMessage(std::move(message));
}
-void MessageBridgeServer::MaybeIncrementInvalidConnectionCount(
- const Node *node) {
- server_status_.increment_invalid_connection_count();
-
- if (node == nullptr) {
- return;
- }
-
- if (!node->has_name()) {
- return;
- }
-
- const aos::Node *client_node = configuration::GetNode(
- event_loop_->configuration(), node->name()->string_view());
-
- if (client_node == nullptr) {
- return;
- }
-
- const int node_index =
- configuration::GetNodeIndex(event_loop_->configuration(), client_node);
-
- ServerConnection *connection =
- server_status_.nodes()[node_index].value().server_connection;
-
- if (connection != nullptr) {
- connection->mutate_invalid_connection_count(
- connection->invalid_connection_count() + 1);
- }
-}
-
void MessageBridgeServer::HandleData(const Message *message) {
VLOG(2) << "Received data of length " << message->size;
@@ -725,7 +701,7 @@
}
server_.Abort(message->header.rcvinfo.rcv_assoc_id);
- MaybeIncrementInvalidConnectionCount(nullptr);
+ server_status_.MaybeIncrementInvalidConnectionCount(nullptr);
return;
}
}
@@ -737,7 +713,7 @@
}
server_.Abort(message->header.rcvinfo.rcv_assoc_id);
- MaybeIncrementInvalidConnectionCount(connect->node());
+ server_status_.MaybeIncrementInvalidConnectionCount(connect->node());
return;
}
@@ -750,7 +726,7 @@
}
server_.Abort(message->header.rcvinfo.rcv_assoc_id);
- MaybeIncrementInvalidConnectionCount(connect->node());
+ server_status_.MaybeIncrementInvalidConnectionCount(connect->node());
return;
}
@@ -762,7 +738,7 @@
}
server_.Abort(message->header.rcvinfo.rcv_assoc_id);
- MaybeIncrementInvalidConnectionCount(connect->node());
+ server_status_.MaybeIncrementInvalidConnectionCount(connect->node());
return;
}
@@ -803,7 +779,7 @@
}
server_.Abort(message->header.rcvinfo.rcv_assoc_id);
- MaybeIncrementInvalidConnectionCount(connect->node());
+ server_status_.MaybeIncrementInvalidConnectionCount(connect->node());
return;
}
++channel_index;
diff --git a/aos/network/message_bridge_server_lib.h b/aos/network/message_bridge_server_lib.h
index b47a4e6..1c3903f 100644
--- a/aos/network/message_bridge_server_lib.h
+++ b/aos/network/message_bridge_server_lib.h
@@ -192,10 +192,6 @@
// received.
void HandleData(const Message *message);
- // Increments the invalid connection count overall, and per node if we know
- // which node (ie, node is not nullptr).
- void MaybeIncrementInvalidConnectionCount(const Node *node);
-
// The maximum number of channels we support on a single connection. We need
// to configure the SCTP socket with this before any clients connect, so we
// need an upper bound on the number of channels any of them will use.
diff --git a/aos/network/message_bridge_server_status.cc b/aos/network/message_bridge_server_status.cc
index 0e8c6b0..e893b40 100644
--- a/aos/network/message_bridge_server_status.cc
+++ b/aos/network/message_bridge_server_status.cc
@@ -90,7 +90,8 @@
} // namespace
MessageBridgeServerStatus::MessageBridgeServerStatus(
- aos::EventLoop *event_loop, std::function<void()> send_data)
+ aos::EventLoop *event_loop,
+ std::function<void(uint32_t, monotonic_clock::time_point)> send_data)
: event_loop_(event_loop),
sender_(event_loop_->MakeSender<ServerStatistics>("/aos")),
statistics_(MakeServerStatistics(
@@ -100,7 +101,7 @@
client_statistics_fetcher_(
event_loop_->MakeFetcher<ClientStatistics>("/aos")),
timestamp_sender_(event_loop_->MakeSender<Timestamp>("/aos")),
- send_data_(send_data) {
+ send_data_(std::move(send_data)) {
server_connection_offsets_.reserve(
statistics_.message().connections()->size());
client_offsets_.reserve(statistics_.message().connections()->size());
@@ -484,7 +485,8 @@
// Since we are building up the timestamp to send here, we need to trigger
// the SendData call ourselves.
if (send_data_) {
- send_data_();
+ send_data_(timestamp_sender_.sent_queue_index(),
+ timestamp_sender_.monotonic_sent_time());
}
}
}
@@ -506,4 +508,48 @@
kPingPeriod);
}
+void MessageBridgeServerStatus::MaybeIncrementInvalidConnectionCount(
+ const Node *node) {
+ increment_invalid_connection_count();
+
+ if (node == nullptr) {
+ return;
+ }
+
+ if (!node->has_name()) {
+ return;
+ }
+
+ const aos::Node *client_node = configuration::GetNode(
+ event_loop_->configuration(), node->name()->string_view());
+
+ if (client_node == nullptr) {
+ return;
+ }
+
+ const int node_index =
+ configuration::GetNodeIndex(event_loop_->configuration(), client_node);
+
+ const std::vector<std::optional<MessageBridgeServerStatus::NodeState>>
+ &server_nodes = nodes();
+ // There is a chance that there is no server node for the given client
+ // `node_index`. This can happen if the other node has a different
+ // configuration such that it starts forwarding messages to the current node,
+ // but the current node's configuration does not expect messages from the
+ // other node. This is likely to happen during a multi-node software update
+ // where the other node has been updated with a different config, while the
+ // current node's update hasn't yet completed. In such cases, we want to
+ // ensure that a server node exists before attempting to access it.
+ if (!server_nodes[node_index]) {
+ return;
+ }
+ ServerConnection *connection =
+ server_nodes[node_index].value().server_connection;
+
+ if (connection != nullptr) {
+ connection->mutate_invalid_connection_count(
+ connection->invalid_connection_count() + 1);
+ }
+}
+
} // namespace aos::message_bridge
diff --git a/aos/network/message_bridge_server_status.h b/aos/network/message_bridge_server_status.h
index 8bdf4a8..11b74a0 100644
--- a/aos/network/message_bridge_server_status.h
+++ b/aos/network/message_bridge_server_status.h
@@ -52,7 +52,8 @@
MessageBridgeServerStatus(
aos::EventLoop *event_loop,
- std::function<void()> send_data = std::function<void()>());
+ std::function<void(uint32_t, monotonic_clock::time_point)> send_data =
+ std::function<void(uint32_t, monotonic_clock::time_point)>());
MessageBridgeServerStatus(const MessageBridgeServerStatus &) = delete;
MessageBridgeServerStatus(MessageBridgeServerStatus &&) = delete;
@@ -60,7 +61,8 @@
delete;
MessageBridgeServerStatus &operator=(MessageBridgeServerStatus &&) = delete;
- void set_send_data(std::function<void()> send_data) {
+ void set_send_data(
+ std::function<void(uint32_t, monotonic_clock::time_point)> send_data) {
send_data_ = send_data;
}
@@ -113,6 +115,10 @@
// connection that got rejected.
void increment_invalid_connection_count() { ++invalid_connection_count_; }
+ // Increments the invalid connection count overall, and per node if we know
+ // which node (ie, node is not nullptr).
+ void MaybeIncrementInvalidConnectionCount(const Node *node);
+
private:
static constexpr std::chrono::nanoseconds kStatisticsPeriod =
std::chrono::seconds(1);
@@ -148,7 +154,7 @@
aos::monotonic_clock::time_point last_statistics_send_time_ =
aos::monotonic_clock::min_time;
- std::function<void()> send_data_;
+ std::function<void(uint32_t, monotonic_clock::time_point)> send_data_;
bool send_ = true;
diff --git a/aos/network/message_bridge_server_status_test.cc b/aos/network/message_bridge_server_status_test.cc
new file mode 100644
index 0000000..8b9aefa
--- /dev/null
+++ b/aos/network/message_bridge_server_status_test.cc
@@ -0,0 +1,44 @@
+#include "aos/network/message_bridge_server_status.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/events/simulated_event_loop.h"
+
+namespace aos::message_bridge::testing {
+
+TEST(MessageBridgeServerStatus, NoThrowOnInvalidServerNode) {
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config(
+ aos::configuration::ReadConfig(
+ "message_bridge_test_combined_timestamps_common_config.json"));
+ aos::SimulatedEventLoopFactory factory(&config.message());
+ // Configure the server node to be `pi1` - for details
+ // on the configuration, refer to
+ // `message_bridge_test_combined_timestamps_common.json`.
+ std::unique_ptr<EventLoop> event_loop =
+ factory.GetNodeEventLoopFactory("pi1")->MakeEventLoop("test");
+ MessageBridgeServerStatus server_status(event_loop.get());
+ // We want to choose a client node such that there is no server for that
+ // client on this node. A simple way to do this is to choose the client node
+ // to be the same as the server node. There will never be a valid `NodeState`
+ // object assigned in `MessageBridgeServerStatus::nodes_`, which is an
+ // `std::vector` of `std::optional<NodeState> elements`. This is because a
+ // node will not be allowed to forward messages to itself since that would
+ // cause a loop of the same message being forwarded over-and-over again. We're
+ // making use of this property to simulate a multi-node software update
+ // scenario in which one node was upgraded to a config that had a valid
+ // connection to another node and started forwarding messages to the other
+ // node. Since the other node was in the process of being updated to the new
+ // software, it did not have the updated config yet, and couldn't find a
+ // server node corresponding to the client node. In this situation,
+ // `MaybeIncrementInvalidConnectionCount()` ended-up accessing an
+ // `std::optional` that was unset. As a regression test, we want to ensure
+ // that no exceptions are raised in this scenario, now that the proper checks
+ // have been added.
+ const aos::Node *client_node =
+ aos::configuration::GetNode(&config.message(), "pi1");
+ EXPECT_NE(client_node, nullptr);
+ EXPECT_NO_THROW(
+ server_status.MaybeIncrementInvalidConnectionCount(client_node));
+}
+
+} // namespace aos::message_bridge::testing
\ No newline at end of file
diff --git a/aos/network/message_bridge_test.cc b/aos/network/message_bridge_test.cc
index c211c89..2dac8ed 100644
--- a/aos/network/message_bridge_test.cc
+++ b/aos/network/message_bridge_test.cc
@@ -45,23 +45,23 @@
// hope for the best. We can be more generous in the future if we need to.
//
// We are faking the application names by passing in --application_name=foo
- OnPi1();
+ pi1_.OnPi();
// Force ourselves to be "raspberrypi" and allocate everything.
- MakePi1Server();
- MakePi1Client();
+ pi1_.MakeServer();
+ pi1_.MakeClient();
const std::string long_data = std::string(10000, 'a');
// And build the app which sends the pings.
FLAGS_application_name = "ping";
- aos::ShmEventLoop ping_event_loop(&config.message());
+ aos::ShmEventLoop ping_event_loop(&config_.message());
aos::Sender<examples::Ping> ping_sender =
ping_event_loop.MakeSender<examples::Ping>("/test");
- aos::ShmEventLoop pi1_test_event_loop(&config.message());
+ aos::ShmEventLoop pi1_test_event_loop_(&pi1_.config_.message());
aos::Fetcher<RemoteMessage> message_header_fetcher1 =
- pi1_test_event_loop.MakeFetcher<RemoteMessage>(
+ pi1_test_event_loop_.MakeFetcher<RemoteMessage>(
shared() ? "/pi1/aos/remote_timestamps/pi2"
: "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping");
@@ -72,18 +72,18 @@
ping_event_loop.MakeFetcher<Timestamp>("/aos");
// Now do it for "raspberrypi2", the client.
- OnPi2();
+ pi2_.OnPi();
- MakePi2Client();
- MakePi2Server();
+ pi2_.MakeClient();
+ pi2_.MakeServer();
// And build the app which sends the pongs.
FLAGS_application_name = "pong";
- aos::ShmEventLoop pong_event_loop(&config.message());
+ aos::ShmEventLoop pong_event_loop(&config_.message());
// And build the app for testing.
FLAGS_application_name = "test";
- aos::ShmEventLoop test_event_loop(&config.message());
+ aos::ShmEventLoop test_event_loop(&config_.message());
aos::Fetcher<ClientStatistics> client_statistics_fetcher =
test_event_loop.MakeFetcher<ClientStatistics>("/aos");
@@ -95,7 +95,7 @@
// Event loop for fetching data delivered to pi2 from pi1 to match up
// messages.
- aos::ShmEventLoop delivered_messages_event_loop(&config.message());
+ aos::ShmEventLoop delivered_messages_event_loop(&config_.message());
aos::Fetcher<Timestamp> pi1_on_pi2_timestamp_fetcher =
delivered_messages_event_loop.MakeFetcher<Timestamp>("/pi1/aos");
aos::Fetcher<examples::Ping> ping_on_pi2_fetcher =
@@ -107,7 +107,7 @@
int pong_count = 0;
pong_event_loop.MakeWatcher("/test", [&pong_count, &pong_event_loop,
this](const examples::Ping &ping) {
- EXPECT_EQ(pong_event_loop.context().source_boot_uuid, pi1_boot_uuid_);
+ EXPECT_EQ(pong_event_loop.context().source_boot_uuid, pi1_.boot_uuid_);
++pong_count;
VLOG(1) << "Got ping back " << FlatbufferToJson(&ping);
});
@@ -139,7 +139,7 @@
}
if (connection->node()->name()->string_view() ==
- pi2_client_event_loop->node()->name()->string_view()) {
+ pi2_.client_event_loop_->node()->name()->string_view()) {
if (connection->state() == State::CONNECTED) {
EXPECT_TRUE(connection->has_boot_uuid());
EXPECT_EQ(connection->connection_count(), 1u);
@@ -344,6 +344,9 @@
chrono::nanoseconds(header.realtime_sent_time()));
const aos::monotonic_clock::time_point header_monotonic_remote_time(
chrono::nanoseconds(header.monotonic_remote_time()));
+ const aos::monotonic_clock::time_point
+ header_monotonic_remote_transmit_time(
+ chrono::nanoseconds(header.monotonic_remote_transmit_time()));
const aos::realtime_clock::time_point header_realtime_remote_time(
chrono::nanoseconds(header.realtime_remote_time()));
@@ -396,12 +399,19 @@
EXPECT_EQ(pi2_context->monotonic_remote_time,
header_monotonic_remote_time);
+ EXPECT_LT(header_monotonic_remote_transmit_time,
+ pi2_context->monotonic_event_time);
+ EXPECT_GT(header_monotonic_remote_transmit_time,
+ pi2_context->monotonic_remote_time);
+
// Confirm the forwarded message also matches the source message.
EXPECT_EQ(pi1_context->queue_index, header.queue_index());
EXPECT_EQ(pi1_context->monotonic_event_time,
header_monotonic_remote_time);
EXPECT_EQ(pi1_context->realtime_event_time,
header_realtime_remote_time);
+ EXPECT_EQ(header_monotonic_remote_transmit_time,
+ pi2_context->monotonic_remote_transmit_time);
});
}
@@ -410,10 +420,10 @@
ThreadedEventLoopRunner pong_thread(&pong_event_loop);
ThreadedEventLoopRunner ping_thread(&ping_event_loop);
- StartPi1Server();
- StartPi1Client();
- StartPi2Client();
- StartPi2Server();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartClient();
+ pi2_.StartServer();
// And go!
// Run for 5 seconds to make sure we have time to estimate the offset.
@@ -442,10 +452,10 @@
// Shut everyone else down before confirming everything actually ran.
ping_thread.Exit();
pong_thread.Exit();
- StopPi1Server();
- StopPi1Client();
- StopPi2Client();
- StopPi2Server();
+ pi1_.StopServer();
+ pi1_.StopClient();
+ pi2_.StopClient();
+ pi2_.StopServer();
// Make sure we sent something.
EXPECT_GE(ping_count, 1);
@@ -479,37 +489,37 @@
// hope for the best. We can be more generous in the future if we need to.
//
// We are faking the application names by passing in --application_name=foo
- OnPi1();
+ pi1_.OnPi();
- MakePi1Server();
- MakePi1Client();
+ pi1_.MakeServer();
+ pi1_.MakeClient();
// And build the app for testing.
- MakePi1Test();
+ pi1_.MakeTest("test1", &pi2_);
aos::Fetcher<ServerStatistics> pi1_server_statistics_fetcher =
- pi1_test_event_loop->MakeFetcher<ServerStatistics>("/pi1/aos");
+ pi1_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi1/aos");
// Now do it for "raspberrypi2", the client.
- OnPi2();
- MakePi2Server();
+ pi2_.OnPi();
+ pi2_.MakeServer();
// And build the app for testing.
- MakePi2Test();
+ pi2_.MakeTest("test2", &pi1_);
aos::Fetcher<ServerStatistics> pi2_server_statistics_fetcher =
- pi2_test_event_loop->MakeFetcher<ServerStatistics>("/pi2/aos");
+ pi2_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi2/aos");
// Wait until we are connected, then send.
- StartPi1Test();
- StartPi2Test();
- StartPi1Server();
- StartPi1Client();
- StartPi2Server();
+ pi1_.StartTest();
+ pi2_.StartTest();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartServer();
{
- MakePi2Client();
+ pi2_.MakeClient();
- RunPi2Client(chrono::milliseconds(3050));
+ pi2_.RunClient(chrono::milliseconds(3050));
// Now confirm we are synchronized.
EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
@@ -540,7 +550,7 @@
chrono::milliseconds(-1));
EXPECT_TRUE(pi2_connection->has_boot_uuid());
- StopPi2Client();
+ pi2_.StopClient();
}
std::this_thread::sleep_for(SctpClientConnection::kReconnectTimeout +
@@ -568,9 +578,9 @@
}
{
- MakePi2Client();
+ pi2_.MakeClient();
// And go!
- RunPi2Client(chrono::milliseconds(3050));
+ pi2_.RunClient(chrono::milliseconds(3050));
EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
EXPECT_TRUE(pi2_server_statistics_fetcher.Fetch());
@@ -605,15 +615,15 @@
<< ": " << FlatbufferToJson(pi2_connection);
EXPECT_TRUE(pi2_connection->has_boot_uuid());
- StopPi2Client();
+ pi2_.StopClient();
}
// Shut everyone else down.
- StopPi1Server();
- StopPi1Client();
- StopPi2Server();
- StopPi1Test();
- StopPi2Test();
+ pi1_.StopServer();
+ pi1_.StopClient();
+ pi2_.StopServer();
+ pi1_.StopTest();
+ pi2_.StopTest();
}
// Test that the server disconnecting triggers the server offsets on the other
@@ -634,41 +644,41 @@
//
// We are faking the application names by passing in --application_name=foo
// Force ourselves to be "raspberrypi" and allocate everything.
- OnPi1();
- MakePi1Server();
- MakePi1Client();
+ pi1_.OnPi();
+ pi1_.MakeServer();
+ pi1_.MakeClient();
// And build the app for testing.
- MakePi1Test();
+ pi1_.MakeTest("test1", &pi2_);
aos::Fetcher<ServerStatistics> pi1_server_statistics_fetcher =
- pi1_test_event_loop->MakeFetcher<ServerStatistics>("/pi1/aos");
+ pi1_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi1/aos");
aos::Fetcher<ClientStatistics> pi1_client_statistics_fetcher =
- pi1_test_event_loop->MakeFetcher<ClientStatistics>("/pi1/aos");
+ pi1_.test_event_loop_->MakeFetcher<ClientStatistics>("/pi1/aos");
// Now do it for "raspberrypi2", the client.
- OnPi2();
- MakePi2Client();
+ pi2_.OnPi();
+ pi2_.MakeClient();
// And build the app for testing.
- MakePi2Test();
+ pi2_.MakeTest("test1", &pi1_);
aos::Fetcher<ServerStatistics> pi2_server_statistics_fetcher =
- pi2_test_event_loop->MakeFetcher<ServerStatistics>("/pi2/aos");
+ pi2_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi2/aos");
// Start everything up. Pong is the only thing we don't know how to wait on,
// so start it first.
- StartPi1Test();
- StartPi2Test();
- StartPi1Server();
- StartPi1Client();
- StartPi2Client();
+ pi1_.StartTest();
+ pi2_.StartTest();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartClient();
// Confirm both client and server statistics messages have decent offsets in
// them.
{
- MakePi2Server();
+ pi2_.MakeServer();
- RunPi2Server(chrono::milliseconds(3050));
+ pi2_.RunServer(chrono::milliseconds(3050));
// Now confirm we are synchronized.
EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
@@ -699,7 +709,7 @@
EXPECT_TRUE(pi2_connection->has_connected_since_time());
EXPECT_EQ(pi2_connection->connection_count(), 1u);
- StopPi2Server();
+ pi2_.StopServer();
}
std::this_thread::sleep_for(std::chrono::seconds(2));
@@ -728,11 +738,11 @@
}
{
- MakePi2Server();
+ pi2_.MakeServer();
// Wait long enough for the client to connect again. It currently takes 3
// seconds of connection to estimate the time offset.
- RunPi2Server(chrono::milliseconds(4050));
+ pi2_.RunServer(chrono::milliseconds(4050));
// And confirm we are synchronized again.
EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
@@ -767,15 +777,15 @@
chrono::milliseconds(-1));
EXPECT_TRUE(pi2_connection->has_boot_uuid());
- StopPi2Server();
+ pi2_.StopServer();
}
// Shut everyone else down.
- StopPi1Server();
- StopPi1Client();
- StopPi2Client();
- StopPi1Test();
- StopPi2Test();
+ pi1_.StopServer();
+ pi1_.StopClient();
+ pi2_.StopClient();
+ pi1_.StopTest();
+ pi2_.StopTest();
}
// TODO(austin): The above test confirms that the external state does the right
@@ -793,10 +803,10 @@
// Tests that when a message is sent before the bridge starts up, but is
// configured as reliable, we forward it. Confirm this survives a client reset.
TEST_P(MessageBridgeParameterizedTest, ReliableSentBeforeClientStartup) {
- OnPi1();
+ pi1_.OnPi();
FLAGS_application_name = "sender";
- aos::ShmEventLoop send_event_loop(&config.message());
+ aos::ShmEventLoop send_event_loop(&config_.message());
aos::Sender<examples::Ping> ping_sender =
send_event_loop.MakeSender<examples::Ping>("/test");
SendPing(&ping_sender, 1);
@@ -804,18 +814,18 @@
send_event_loop.MakeSender<examples::Ping>("/unreliable");
SendPing(&unreliable_ping_sender, 1);
- MakePi1Server();
- MakePi1Client();
+ pi1_.MakeServer();
+ pi1_.MakeClient();
FLAGS_application_name = "pi1_timestamp";
- aos::ShmEventLoop pi1_remote_timestamp_event_loop(&config.message());
+ aos::ShmEventLoop pi1_remote_timestamp_event_loop(&config_.message());
// Now do it for "raspberrypi2", the client.
- OnPi2();
+ pi2_.OnPi();
- MakePi2Server();
+ pi2_.MakeServer();
- aos::ShmEventLoop receive_event_loop(&config.message());
+ aos::ShmEventLoop receive_event_loop(&config_.message());
aos::Fetcher<examples::Ping> ping_fetcher =
receive_event_loop.MakeFetcher<examples::Ping>("/test");
aos::Fetcher<examples::Ping> unreliable_ping_fetcher =
@@ -850,9 +860,9 @@
EXPECT_FALSE(unreliable_ping_fetcher.Fetch());
// Spin up the persistent pieces.
- StartPi1Server();
- StartPi1Client();
- StartPi2Server();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartServer();
// Event used to wait for the timestamp counting thread to start.
std::unique_ptr<ThreadedEventLoopRunner> pi1_remote_timestamp_thread =
@@ -860,10 +870,12 @@
&pi1_remote_timestamp_event_loop);
{
+ const aos::monotonic_clock::time_point startup_time =
+ aos::monotonic_clock::now();
// Now spin up a client for 2 seconds.
- MakePi2Client();
+ pi2_.MakeClient();
- RunPi2Client(chrono::milliseconds(2050));
+ pi2_.RunClient(chrono::milliseconds(2050));
// Confirm there is no detected duplicate packet.
EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
@@ -878,17 +890,21 @@
0u);
EXPECT_TRUE(ping_fetcher.Fetch());
+ EXPECT_GT(ping_fetcher.context().monotonic_remote_transmit_time,
+ startup_time);
+ EXPECT_LT(ping_fetcher.context().monotonic_remote_transmit_time,
+ aos::monotonic_clock::now());
EXPECT_FALSE(unreliable_ping_fetcher.Fetch());
EXPECT_EQ(ping_timestamp_count, 1);
- StopPi2Client();
+ pi2_.StopClient();
}
{
// Now, spin up a client for 2 seconds.
- MakePi2Client();
+ pi2_.MakeClient();
- RunPi2Client(chrono::milliseconds(5050));
+ pi2_.RunClient(chrono::milliseconds(5050));
// Confirm we detect the duplicate packet correctly.
EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
@@ -906,14 +922,14 @@
EXPECT_FALSE(ping_fetcher.Fetch());
EXPECT_FALSE(unreliable_ping_fetcher.Fetch());
- StopPi2Client();
+ pi2_.StopClient();
}
// Shut everyone else down.
- StopPi1Client();
- StopPi2Server();
+ pi1_.StopClient();
+ pi2_.StopServer();
pi1_remote_timestamp_thread.reset();
- StopPi1Server();
+ pi1_.StopServer();
}
// Tests that when a message is sent before the bridge starts up, but is
@@ -921,12 +937,12 @@
// resets.
TEST_P(MessageBridgeParameterizedTest, ReliableSentBeforeServerStartup) {
// Now do it for "raspberrypi2", the client.
- OnPi2();
+ pi2_.OnPi();
- MakePi2Server();
- MakePi2Client();
+ pi2_.MakeServer();
+ pi2_.MakeClient();
- aos::ShmEventLoop receive_event_loop(&config.message());
+ aos::ShmEventLoop receive_event_loop(&config_.message());
aos::Fetcher<examples::Ping> ping_fetcher =
receive_event_loop.MakeFetcher<examples::Ping>("/test");
aos::Fetcher<examples::Ping> unreliable_ping_fetcher =
@@ -935,10 +951,10 @@
receive_event_loop.MakeFetcher<ClientStatistics>("/pi2/aos");
// Force ourselves to be "raspberrypi" and allocate everything.
- OnPi1();
+ pi1_.OnPi();
FLAGS_application_name = "sender";
- aos::ShmEventLoop send_event_loop(&config.message());
+ aos::ShmEventLoop send_event_loop(&config_.message());
aos::Sender<examples::Ping> ping_sender =
send_event_loop.MakeSender<examples::Ping>("/test");
{
@@ -949,10 +965,10 @@
builder.CheckOk(builder.Send(ping_builder.Finish()));
}
- MakePi1Client();
+ pi1_.MakeClient();
FLAGS_application_name = "pi1_timestamp";
- aos::ShmEventLoop pi1_remote_timestamp_event_loop(&config.message());
+ aos::ShmEventLoop pi1_remote_timestamp_event_loop(&config_.message());
const size_t ping_channel_index = configuration::ChannelIndex(
receive_event_loop.configuration(), ping_fetcher.channel());
@@ -981,19 +997,21 @@
EXPECT_FALSE(unreliable_ping_fetcher.Fetch());
// Spin up the persistent pieces.
- StartPi1Client();
- StartPi2Server();
- StartPi2Client();
+ pi1_.StartClient();
+ pi2_.StartServer();
+ pi2_.StartClient();
std::unique_ptr<ThreadedEventLoopRunner> pi1_remote_timestamp_thread =
std::make_unique<ThreadedEventLoopRunner>(
&pi1_remote_timestamp_event_loop);
{
+ const aos::monotonic_clock::time_point startup_time =
+ aos::monotonic_clock::now();
// Now, spin up a server for 2 seconds.
- MakePi1Server();
+ pi1_.MakeServer();
- RunPi1Server(chrono::milliseconds(2050));
+ pi1_.RunServer(chrono::milliseconds(2050));
// Confirm there is no detected duplicate packet.
EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
@@ -1008,18 +1026,23 @@
0u);
EXPECT_TRUE(ping_fetcher.Fetch());
+ EXPECT_GT(ping_fetcher.context().monotonic_remote_transmit_time,
+ startup_time);
+ EXPECT_LT(ping_fetcher.context().monotonic_remote_transmit_time,
+ aos::monotonic_clock::now());
+
EXPECT_FALSE(unreliable_ping_fetcher.Fetch());
EXPECT_EQ(ping_timestamp_count, 1);
LOG(INFO) << "Shutting down first pi1 MessageBridgeServer";
- StopPi1Server();
+ pi1_.StopServer();
}
{
// Now, spin up a second server for 2 seconds.
- MakePi1Server();
+ pi1_.MakeServer();
- RunPi1Server(chrono::milliseconds(2050));
+ pi1_.RunServer(chrono::milliseconds(2050));
// Confirm we detect the duplicate packet correctly.
EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
@@ -1037,13 +1060,13 @@
EXPECT_FALSE(ping_fetcher.Fetch());
EXPECT_FALSE(unreliable_ping_fetcher.Fetch());
- StopPi1Server();
+ pi1_.StopServer();
}
// Shut everyone else down.
- StopPi1Client();
- StopPi2Server();
- StopPi2Client();
+ pi1_.StopClient();
+ pi2_.StopServer();
+ pi2_.StopClient();
pi1_remote_timestamp_thread.reset();
}
@@ -1052,27 +1075,27 @@
// client. This ensures that we handle a disconnecting & reconnecting client
// correctly in the server reliable connection retry logic.
TEST_P(MessageBridgeParameterizedTest, ReliableSentDuringClientReboot) {
- OnPi1();
+ pi1_.OnPi();
FLAGS_application_name = "sender";
- aos::ShmEventLoop send_event_loop(&config.message());
+ aos::ShmEventLoop send_event_loop(&config_.message());
aos::Sender<examples::Ping> ping_sender =
send_event_loop.MakeSender<examples::Ping>("/test");
size_t ping_index = 0;
SendPing(&ping_sender, ++ping_index);
- MakePi1Server();
- MakePi1Client();
+ pi1_.MakeServer();
+ pi1_.MakeClient();
FLAGS_application_name = "pi1_timestamp";
- aos::ShmEventLoop pi1_remote_timestamp_event_loop(&config.message());
+ aos::ShmEventLoop pi1_remote_timestamp_event_loop(&config_.message());
// Now do it for "raspberrypi2", the client.
- OnPi2();
+ pi2_.OnPi();
- MakePi2Server();
+ pi2_.MakeServer();
- aos::ShmEventLoop receive_event_loop(&config.message());
+ aos::ShmEventLoop receive_event_loop(&config_.message());
aos::Fetcher<examples::Ping> ping_fetcher =
receive_event_loop.MakeFetcher<examples::Ping>("/test");
aos::Fetcher<ClientStatistics> pi2_client_statistics_fetcher =
@@ -1104,9 +1127,9 @@
EXPECT_FALSE(ping_fetcher.Fetch());
// Spin up the persistent pieces.
- StartPi1Server();
- StartPi1Client();
- StartPi2Server();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartServer();
// Event used to wait for the timestamp counting thread to start.
std::unique_ptr<ThreadedEventLoopRunner> pi1_remote_timestamp_thread =
@@ -1115,9 +1138,9 @@
{
// Now, spin up a client for 2 seconds.
- MakePi2Client();
+ pi2_.MakeClient();
- RunPi2Client(chrono::milliseconds(2050));
+ pi2_.RunClient(chrono::milliseconds(2050));
// Confirm there is no detected duplicate packet.
EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
@@ -1134,7 +1157,7 @@
EXPECT_TRUE(ping_fetcher.Fetch());
EXPECT_EQ(ping_timestamp_count, 1);
- StopPi2Client();
+ pi2_.StopClient();
}
// Send some reliable messages while the client is dead. Only the final one
@@ -1144,10 +1167,12 @@
}
{
+ const aos::monotonic_clock::time_point startup_time =
+ aos::monotonic_clock::now();
// Now, spin up a client for 2 seconds.
- MakePi2Client();
+ pi2_.MakeClient();
- RunPi2Client(chrono::milliseconds(5050));
+ pi2_.RunClient(chrono::milliseconds(5050));
// No duplicate packets should have appeared.
EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
@@ -1165,17 +1190,22 @@
// We should have gotten precisely one more ping message--the latest one
// sent should've made it, but no previous ones.
EXPECT_TRUE(ping_fetcher.FetchNext());
+ EXPECT_GT(ping_fetcher.context().monotonic_remote_transmit_time,
+ startup_time);
+ EXPECT_LT(ping_fetcher.context().monotonic_remote_transmit_time,
+ aos::monotonic_clock::now());
+
EXPECT_EQ(ping_index, ping_fetcher->value());
EXPECT_FALSE(ping_fetcher.FetchNext());
- StopPi2Client();
+ pi2_.StopClient();
}
// Shut everyone else down.
- StopPi1Client();
- StopPi2Server();
+ pi1_.StopClient();
+ pi2_.StopServer();
pi1_remote_timestamp_thread.reset();
- StopPi1Server();
+ pi1_.StopServer();
}
// Test that differing config sha256's result in no connection.
@@ -1194,42 +1224,42 @@
// hope for the best. We can be more generous in the future if we need to.
//
// We are faking the application names by passing in --application_name=foo
- OnPi1();
+ pi1_.OnPi();
- MakePi1Server(
+ pi1_.MakeServer(
"dummy sha256 ");
- MakePi1Client();
+ pi1_.MakeClient();
// And build the app for testing.
- MakePi1Test();
+ pi1_.MakeTest("test1", &pi2_);
aos::Fetcher<ServerStatistics> pi1_server_statistics_fetcher =
- pi1_test_event_loop->MakeFetcher<ServerStatistics>("/pi1/aos");
+ pi1_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi1/aos");
aos::Fetcher<ClientStatistics> pi1_client_statistics_fetcher =
- pi1_test_event_loop->MakeFetcher<ClientStatistics>("/pi1/aos");
+ pi1_.test_event_loop_->MakeFetcher<ClientStatistics>("/pi1/aos");
// Now do it for "raspberrypi2", the client.
- OnPi2();
- MakePi2Server();
+ pi2_.OnPi();
+ pi2_.MakeServer();
// And build the app for testing.
- MakePi2Test();
+ pi2_.MakeTest("test1", &pi1_);
aos::Fetcher<ServerStatistics> pi2_server_statistics_fetcher =
- pi2_test_event_loop->MakeFetcher<ServerStatistics>("/pi2/aos");
+ pi2_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi2/aos");
aos::Fetcher<ClientStatistics> pi2_client_statistics_fetcher =
- pi2_test_event_loop->MakeFetcher<ClientStatistics>("/pi2/aos");
+ pi2_.test_event_loop_->MakeFetcher<ClientStatistics>("/pi2/aos");
// Wait until we are connected, then send.
- StartPi1Test();
- StartPi2Test();
- StartPi1Server();
- StartPi1Client();
- StartPi2Server();
+ pi1_.StartTest();
+ pi2_.StartTest();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartServer();
{
- MakePi2Client();
+ pi2_.MakeClient();
- RunPi2Client(chrono::milliseconds(3050));
+ pi2_.RunClient(chrono::milliseconds(3050));
// Now confirm we are synchronized.
EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
@@ -1270,15 +1300,15 @@
VLOG(1) << aos::FlatbufferToJson(pi2_client_statistics_fetcher.get());
VLOG(1) << aos::FlatbufferToJson(pi1_client_statistics_fetcher.get());
- StopPi2Client();
+ pi2_.StopClient();
}
// Shut everyone else down.
- StopPi1Server();
- StopPi1Client();
- StopPi2Server();
- StopPi1Test();
- StopPi2Test();
+ pi1_.StopServer();
+ pi1_.StopClient();
+ pi2_.StopServer();
+ pi1_.StopTest();
+ pi2_.StopTest();
}
// Test that a client which connects with too big a message gets disconnected
@@ -1298,54 +1328,54 @@
// hope for the best. We can be more generous in the future if we need to.
//
// We are faking the application names by passing in --application_name=foo
- OnPi1();
+ pi1_.OnPi();
- MakePi1Server();
- MakePi1Client();
+ pi1_.MakeServer();
+ pi1_.MakeClient();
// And build the app for testing.
- MakePi1Test();
+ pi1_.MakeTest("test1", &pi2_);
aos::Fetcher<ServerStatistics> pi1_server_statistics_fetcher =
- pi1_test_event_loop->MakeFetcher<ServerStatistics>("/pi1/aos");
+ pi1_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi1/aos");
aos::Fetcher<ClientStatistics> pi1_client_statistics_fetcher =
- pi1_test_event_loop->MakeFetcher<ClientStatistics>("/pi1/aos");
+ pi1_.test_event_loop_->MakeFetcher<ClientStatistics>("/pi1/aos");
// Now do it for "raspberrypi2", the client.
- OnPi2();
- MakePi2Server();
+ pi2_.OnPi();
+ pi2_.MakeServer();
// And build the app for testing.
- MakePi2Test();
+ pi2_.MakeTest("test1", &pi1_);
aos::Fetcher<ServerStatistics> pi2_server_statistics_fetcher =
- pi2_test_event_loop->MakeFetcher<ServerStatistics>("/pi2/aos");
+ pi2_.test_event_loop_->MakeFetcher<ServerStatistics>("/pi2/aos");
aos::Fetcher<ClientStatistics> pi2_client_statistics_fetcher =
- pi2_test_event_loop->MakeFetcher<ClientStatistics>("/pi2/aos");
+ pi2_.test_event_loop_->MakeFetcher<ClientStatistics>("/pi2/aos");
// Wait until we are connected, then send.
- StartPi1Test();
- StartPi2Test();
- StartPi1Server();
- StartPi1Client();
- StartPi2Server();
+ pi1_.StartTest();
+ pi2_.StartTest();
+ pi1_.StartServer();
+ pi1_.StartClient();
+ pi2_.StartServer();
{
// Now, spin up a SctpClient and send a massive hunk of data. This should
// trigger a disconnect, but no crash.
- OnPi2();
+ pi2_.OnPi();
FLAGS_application_name = "pi2_message_bridge_client";
- pi2_client_event_loop =
- std::make_unique<aos::ShmEventLoop>(&config.message());
- pi2_client_event_loop->SetRuntimeRealtimePriority(1);
+ pi2_.client_event_loop_ =
+ std::make_unique<aos::ShmEventLoop>(&config_.message());
+ pi2_.client_event_loop_->SetRuntimeRealtimePriority(1);
- const aos::Node *const remote_node = CHECK_NOTNULL(
- configuration::GetNode(pi2_client_event_loop->configuration(), "pi1"));
+ const aos::Node *const remote_node = CHECK_NOTNULL(configuration::GetNode(
+ pi2_.client_event_loop_->configuration(), "pi1"));
const aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect>
connect_message(MakeConnectMessage(
- pi2_client_event_loop->configuration(),
- pi2_client_event_loop->node(), "pi1",
- pi2_client_event_loop->boot_uuid(), config_sha256));
+ pi2_.client_event_loop_->configuration(),
+ pi2_.client_event_loop_->node(), "pi1",
+ pi2_.client_event_loop_->boot_uuid(), config_sha256_));
SctpClient client(remote_node->hostname()->string_view(),
remote_node->port(),
@@ -1369,20 +1399,21 @@
const std::string big_data(kBigMessageSize, 'a');
- pi2_client_event_loop->epoll()->OnReadable(client.fd(), [&]() {
+ pi2_.client_event_loop_->epoll()->OnReadable(client.fd(), [&]() {
aos::unique_c_ptr<Message> message = client.Read();
client.FreeMessage(std::move(message));
});
- aos::TimerHandler *const send_big_message = pi2_client_event_loop->AddTimer(
- [&]() { CHECK(client.Send(kConnectStream(), big_data, 0)); });
+ aos::TimerHandler *const send_big_message =
+ pi2_.client_event_loop_->AddTimer(
+ [&]() { CHECK(client.Send(kConnectStream(), big_data, 0)); });
- pi2_client_event_loop->OnRun([this, send_big_message]() {
- send_big_message->Schedule(pi2_client_event_loop->monotonic_now() +
+ pi2_.client_event_loop_->OnRun([this, send_big_message]() {
+ send_big_message->Schedule(pi2_.client_event_loop_->monotonic_now() +
chrono::seconds(1));
});
- RunPi2Client(chrono::milliseconds(3050));
+ pi2_.RunClient(chrono::milliseconds(3050));
// Now confirm we are synchronized.
EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
@@ -1417,17 +1448,17 @@
VLOG(1) << aos::FlatbufferToJson(pi1_server_statistics_fetcher.get());
VLOG(1) << aos::FlatbufferToJson(pi1_client_statistics_fetcher.get());
- pi2_client_event_loop->epoll()->DeleteFd(client.fd());
+ pi2_.client_event_loop_->epoll()->DeleteFd(client.fd());
- StopPi2Client();
+ pi2_.StopClient();
}
// Shut everyone else down.
- StopPi1Server();
- StopPi1Client();
- StopPi2Server();
- StopPi1Test();
- StopPi2Test();
+ pi1_.StopServer();
+ pi1_.StopClient();
+ pi2_.StopServer();
+ pi1_.StopTest();
+ pi2_.StopTest();
}
INSTANTIATE_TEST_SUITE_P(
@@ -1437,4 +1468,71 @@
true},
Param{"message_bridge_test_common_config.json", false}));
+// Tests the case in which the configurations for the server and client are
+// different - specifically the case where the client's config allows it to
+// "talk" to the server, while the server's config does not allow the client to
+// "talk" to it. The expectation in such a case is that we don't crash or raise
+// an exception.
+TEST(MessageBridgeTests, MismatchedServerAndClientConfigs) {
+ // Make a `MessageBridgeServer` with the config
+ // `message_bridge_test_mismatched_configs_pi1_and_pi3_config.json`.
+ // In this config, `pi1` talks to `pi3`, but does *not* talk to `pi2`.
+ PiNode pi1("pi1", "raspberrypi", "pi1_message_bridge_server",
+ "message_bridge_test_mismatched_configs_pi1_and_pi3_config.json");
+ pi1.OnPi();
+ pi1.MakeServer();
+ aos::ShmEventLoop pi1_test_event_loop(&pi1.config_.message());
+ aos::Fetcher<ServerStatistics> pi1_server_statistics_fetcher =
+ pi1_test_event_loop.MakeFetcher<ServerStatistics>("/pi1/aos");
+
+ // Make a `MessageBridgeClient` with the config
+ // `message_bridge_test_mismatched_configs_pi1_and_pi2_config.json`.
+ // In this config, `pi1` talks to `pi2`.
+ // Reasoning:
+ // Due to this mismatch between the configs of the server and client,
+ // when the client `pi2` sends a "connect" request to the server `pi1`,
+ // there will be no server node placed in the
+ // `MessageBridgeServerStatus::nodes_` vector at the index corresponding to
+ // the client node's index. In such a case, we expect to not crash or raise an
+ // exception.
+ PiNode pi2("pi2", "raspberrypi2", "pi2_message_bridge_client",
+ "message_bridge_test_mismatched_configs_pi1_and_pi2_config.json");
+ pi2.OnPi();
+ pi2.MakeClient();
+
+ // Put the server and client on 2 separate threaded runners and start running.
+ pi1.StartServer();
+ pi2.StartClient();
+
+ // Sleep here while the server and client threads run for 1 second.
+ // During this time, the client will attempt to connect to the server.
+ // We've set them up with mismatching configs such that the
+ // server does not expect to talk to the client, but the client does
+ // expect to connect to the server.
+ // We expect that neither of the threads crashes/raises an exception.
+ // If any of them does, the test terminates and the exception is reported
+ // via the stack trace when running the test.
+ std::this_thread::sleep_for(chrono::milliseconds(1000));
+
+ EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
+ // Since pi1's configuration is such that it expects to talk only to pi3,
+ // we expect the number of connections to be 1, and the node to
+ // be `pi3`.
+ EXPECT_EQ(pi1_server_statistics_fetcher->connections()->size(), 1);
+ const ServerConnection *const pi1_connection =
+ pi1_server_statistics_fetcher->connections()->Get(0);
+ EXPECT_EQ(pi1_connection->node()->name()->string_view(), "pi3");
+ // Since we didn't really spawn a `pi3` node in this test, we expect
+ // that the connection is disconnected, and the connection count is 0.
+ EXPECT_EQ(pi1_connection->state(), State::DISCONNECTED);
+ EXPECT_EQ(pi1_connection->connection_count(), 0u);
+ // Also, since no connection was established, we expect that there is
+ // no `connected_since_time` set.
+ EXPECT_FALSE(pi1_connection->has_connected_since_time());
+
+ // If we got here, everything went well. Stop the threads.
+ pi1.StopServer();
+ pi2.StopClient();
+}
+
} // namespace aos::message_bridge::testing
diff --git a/aos/network/message_bridge_test_lib.cc b/aos/network/message_bridge_test_lib.cc
index 0062bfd..6b8b49e 100644
--- a/aos/network/message_bridge_test_lib.cc
+++ b/aos/network/message_bridge_test_lib.cc
@@ -40,249 +40,159 @@
}
MessageBridgeParameterizedTest::MessageBridgeParameterizedTest()
- : config(aos::configuration::ReadConfig(
- ArtifactPath(absl::StrCat("aos/network/", GetParam().config)))),
- config_sha256(Sha256(config.span())),
- pi1_boot_uuid_(UUID::Random()),
- pi2_boot_uuid_(UUID::Random()) {
+ : pi1_("pi1", "raspberrypi", "pi1_message_bridge_server",
+ GetParam().config),
+ pi2_("pi2", "raspberrypi2", "pi2_message_bridge_client",
+ GetParam().config),
+ config_(aos::configuration::ReadConfig(GetParam().config)),
+ config_sha256_(Sha256(config_.span())) {
// Make sure that we clean up all the shared memory queues so that we cannot
// inadvertently be influenced other tests or by previously run AOS
// applications (in a fully sharded test running inside the bazel sandbox,
// this should not matter).
- util::UnlinkRecursive(ShmBase("pi1"));
- util::UnlinkRecursive(ShmBase("pi2"));
+ util::UnlinkRecursive(ShmBase(pi1_.node_name_));
+ util::UnlinkRecursive(ShmBase(pi2_.node_name_));
}
bool MessageBridgeParameterizedTest::shared() const {
return GetParam().shared;
}
-void MessageBridgeParameterizedTest::OnPi1() {
- DoSetShmBase("pi1");
- FLAGS_override_hostname = "raspberrypi";
- FLAGS_boot_uuid = pi1_boot_uuid_.ToString();
+PiNode::PiNode(const std::string node_name, const std::string host_name,
+ const std::string app_name, const std::string config_filename)
+ : boot_uuid_(UUID::Random()),
+ node_name_(node_name),
+ host_name_(host_name),
+ app_name_(app_name),
+ config_(aos::configuration::ReadConfig(config_filename)),
+ config_sha256_(Sha256(config_.span())) {}
+
+void PiNode::OnPi() {
+ DoSetShmBase(node_name_);
+ FLAGS_override_hostname = host_name_;
+ FLAGS_boot_uuid = boot_uuid_.ToString();
}
-void MessageBridgeParameterizedTest::OnPi2() {
- DoSetShmBase("pi2");
- FLAGS_override_hostname = "raspberrypi2";
- FLAGS_boot_uuid = pi2_boot_uuid_.ToString();
-}
-
-void MessageBridgeParameterizedTest::MakePi1Server(
- std::string server_config_sha256) {
- OnPi1();
- LOG(INFO) << "Making pi1 server";
- FLAGS_application_name = "pi1_message_bridge_server";
- pi1_server_event_loop =
- std::make_unique<aos::ShmEventLoop>(&config.message());
- pi1_server_event_loop->SetRuntimeRealtimePriority(1);
- pi1_message_bridge_server = std::make_unique<MessageBridgeServer>(
- pi1_server_event_loop.get(),
- server_config_sha256.size() == 0 ? config_sha256 : server_config_sha256,
+void PiNode::MakeServer(const std::string server_config_sha256) {
+ OnPi();
+ LOG(INFO) << "Making " << node_name_ << " server";
+ FLAGS_application_name = app_name_;
+ server_event_loop_ = std::make_unique<aos::ShmEventLoop>(&config_.message());
+ server_event_loop_->SetRuntimeRealtimePriority(1);
+ message_bridge_server_ = std::make_unique<MessageBridgeServer>(
+ server_event_loop_.get(),
+ server_config_sha256.size() == 0 ? config_sha256_ : server_config_sha256,
SctpAuthMethod::kNoAuth);
}
-void MessageBridgeParameterizedTest::RunPi1Server(
- chrono::nanoseconds duration) {
- LOG(INFO) << "Running pi1 server";
+void PiNode::RunServer(const chrono::nanoseconds duration) {
+ LOG(INFO) << "Running " << node_name_ << " server";
// Set up a shutdown callback.
- aos::TimerHandler *const quit = pi1_server_event_loop->AddTimer(
- [this]() { pi1_server_event_loop->Exit(); });
- pi1_server_event_loop->OnRun([this, quit, duration]() {
+ aos::TimerHandler *const quit =
+ server_event_loop_->AddTimer([this]() { server_event_loop_->Exit(); });
+ server_event_loop_->OnRun([this, quit, duration]() {
// Stop between timestamps, not exactly on them.
- quit->Schedule(pi1_server_event_loop->monotonic_now() + duration);
+ quit->Schedule(server_event_loop_->monotonic_now() + duration);
});
- pi1_server_event_loop->Run();
+ server_event_loop_->Run();
}
-void MessageBridgeParameterizedTest::StartPi1Server() {
- LOG(INFO) << "Starting pi1 server";
- pi1_server_thread =
- std::make_unique<ThreadedEventLoopRunner>(pi1_server_event_loop.get());
+void PiNode::StartServer() {
+ LOG(INFO) << "Starting " << node_name_ << " server";
+ server_thread_ =
+ std::make_unique<ThreadedEventLoopRunner>(server_event_loop_.get());
}
-void MessageBridgeParameterizedTest::StopPi1Server() {
- LOG(INFO) << "Stopping pi1 server";
- pi1_server_thread.reset();
- pi1_message_bridge_server.reset();
- pi1_server_event_loop.reset();
+void PiNode::StopServer() {
+ LOG(INFO) << "Stopping " << node_name_ << " server";
+ server_thread_.reset();
+ message_bridge_server_.reset();
+ server_event_loop_.reset();
}
-void MessageBridgeParameterizedTest::MakePi1Client() {
- OnPi1();
- LOG(INFO) << "Making pi1 client";
- FLAGS_application_name = "pi1_message_bridge_client";
- pi1_client_event_loop =
- std::make_unique<aos::ShmEventLoop>(&config.message());
- pi1_client_event_loop->SetRuntimeRealtimePriority(1);
- pi1_message_bridge_client = std::make_unique<MessageBridgeClient>(
- pi1_client_event_loop.get(), config_sha256, SctpAuthMethod::kNoAuth);
+void PiNode::MakeClient() {
+ OnPi();
+ LOG(INFO) << "Making " << node_name_ << " client";
+ FLAGS_application_name = app_name_;
+ client_event_loop_ = std::make_unique<aos::ShmEventLoop>(&config_.message());
+ client_event_loop_->SetRuntimeRealtimePriority(1);
+ message_bridge_client_ = std::make_unique<MessageBridgeClient>(
+ client_event_loop_.get(), config_sha256_, SctpAuthMethod::kNoAuth);
}
-void MessageBridgeParameterizedTest::StartPi1Client() {
- LOG(INFO) << "Starting pi1 client";
- pi1_client_thread =
- std::make_unique<ThreadedEventLoopRunner>(pi1_client_event_loop.get());
+void PiNode::StartClient() {
+ LOG(INFO) << "Starting " << node_name_ << " client";
+ client_thread_ =
+ std::make_unique<ThreadedEventLoopRunner>(client_event_loop_.get());
}
-void MessageBridgeParameterizedTest::StopPi1Client() {
- LOG(INFO) << "Stopping pi1 client";
- pi1_client_thread.reset();
- pi1_message_bridge_client.reset();
- pi1_client_event_loop.reset();
+void PiNode::StopClient() {
+ LOG(INFO) << "Stopping " << node_name_ << " client";
+ client_thread_.reset();
+ message_bridge_client_.reset();
+ client_event_loop_.reset();
}
-void MessageBridgeParameterizedTest::MakePi1Test() {
- OnPi1();
- LOG(INFO) << "Making pi1 test";
- FLAGS_application_name = "test1";
- pi1_test_event_loop = std::make_unique<aos::ShmEventLoop>(&config.message());
+void PiNode::MakeTest(const std::string test_app_name,
+ const PiNode *other_node) {
+ OnPi();
+ LOG(INFO) << "Making " << node_name_ << " test";
+ FLAGS_application_name = test_app_name;
+ test_event_loop_ = std::make_unique<aos::ShmEventLoop>(&config_.message());
- pi1_test_event_loop->MakeWatcher(
- "/pi1/aos", [](const ServerStatistics &stats) {
- VLOG(1) << "/pi1/aos ServerStatistics " << FlatbufferToJson(&stats);
+ std::string channel_name = "/" + node_name_ + "/aos";
+ test_event_loop_->MakeWatcher(
+ channel_name, [channel_name](const ServerStatistics &stats) {
+ VLOG(1) << channel_name << " ServerStatistics "
+ << FlatbufferToJson(&stats);
});
- pi1_test_event_loop->MakeWatcher(
- "/pi1/aos", [](const ClientStatistics &stats) {
- VLOG(1) << "/pi1/aos ClientStatistics " << FlatbufferToJson(&stats);
+ test_event_loop_->MakeWatcher(
+ channel_name, [channel_name](const ClientStatistics &stats) {
+ VLOG(1) << channel_name << " ClientStatistics "
+ << FlatbufferToJson(&stats);
});
- pi1_test_event_loop->MakeWatcher("/pi1/aos", [](const Timestamp ×tamp) {
- VLOG(1) << "/pi1/aos Timestamp " << FlatbufferToJson(×tamp);
- });
- pi1_test_event_loop->MakeWatcher("/pi2/aos", [this](
- const Timestamp ×tamp) {
- VLOG(1) << "/pi2/aos Timestamp " << FlatbufferToJson(×tamp);
- EXPECT_EQ(pi1_test_event_loop->context().source_boot_uuid, pi2_boot_uuid_);
- });
+ test_event_loop_->MakeWatcher(channel_name,
+ [channel_name](const Timestamp ×tamp) {
+ VLOG(1) << channel_name << " Timestamp "
+ << FlatbufferToJson(×tamp);
+ });
+ std::string other_channel_name = "/" + other_node->node_name_ + "/aos";
+ test_event_loop_->MakeWatcher(
+ other_channel_name,
+ [this, other_channel_name, other_node](const Timestamp ×tamp) {
+ VLOG(1) << other_channel_name << " Timestamp "
+ << FlatbufferToJson(×tamp);
+ EXPECT_EQ(test_event_loop_->context().source_boot_uuid,
+ other_node->boot_uuid_);
+ });
}
-void MessageBridgeParameterizedTest::StartPi1Test() {
- LOG(INFO) << "Starting pi1 test";
- pi1_test_thread =
- std::make_unique<ThreadedEventLoopRunner>(pi1_test_event_loop.get());
+void PiNode::StartTest() {
+ LOG(INFO) << "Starting " << node_name_ << " test";
+ test_thread_ =
+ std::make_unique<ThreadedEventLoopRunner>(test_event_loop_.get());
}
-void MessageBridgeParameterizedTest::StopPi1Test() {
- LOG(INFO) << "Stopping pi1 test";
- pi1_test_thread.reset();
+void PiNode::StopTest() {
+ LOG(INFO) << "Stopping " << node_name_ << " test";
+ test_thread_.reset();
}
-void MessageBridgeParameterizedTest::MakePi2Server() {
- OnPi2();
- LOG(INFO) << "Making pi2 server";
- FLAGS_application_name = "pi2_message_bridge_server";
- pi2_server_event_loop =
- std::make_unique<aos::ShmEventLoop>(&config.message());
- pi2_server_event_loop->SetRuntimeRealtimePriority(1);
- pi2_message_bridge_server = std::make_unique<MessageBridgeServer>(
- pi2_server_event_loop.get(), config_sha256, SctpAuthMethod::kNoAuth);
-}
-
-void MessageBridgeParameterizedTest::RunPi2Server(
- chrono::nanoseconds duration) {
- LOG(INFO) << "Running pi2 server";
- // Schedule a shutdown callback.
- aos::TimerHandler *const quit = pi2_server_event_loop->AddTimer(
- [this]() { pi2_server_event_loop->Exit(); });
- pi2_server_event_loop->OnRun([this, quit, duration]() {
- // Stop between timestamps, not exactly on them.
- quit->Schedule(pi2_server_event_loop->monotonic_now() + duration);
- });
-
- pi2_server_event_loop->Run();
-}
-
-void MessageBridgeParameterizedTest::StartPi2Server() {
- LOG(INFO) << "Starting pi2 server";
- pi2_server_thread =
- std::make_unique<ThreadedEventLoopRunner>(pi2_server_event_loop.get());
-}
-
-void MessageBridgeParameterizedTest::StopPi2Server() {
- LOG(INFO) << "Stopping pi2 server";
- pi2_server_thread.reset();
- pi2_message_bridge_server.reset();
- pi2_server_event_loop.reset();
-}
-
-void MessageBridgeParameterizedTest::MakePi2Client() {
- OnPi2();
- LOG(INFO) << "Making pi2 client";
- FLAGS_application_name = "pi2_message_bridge_client";
- pi2_client_event_loop =
- std::make_unique<aos::ShmEventLoop>(&config.message());
- pi2_client_event_loop->SetRuntimeRealtimePriority(1);
- pi2_message_bridge_client = std::make_unique<MessageBridgeClient>(
- pi2_client_event_loop.get(), config_sha256, SctpAuthMethod::kNoAuth);
-}
-
-void MessageBridgeParameterizedTest::RunPi2Client(
- chrono::nanoseconds duration) {
+void PiNode::RunClient(const chrono::nanoseconds duration) {
LOG(INFO) << "Running pi2 client";
// Run for 5 seconds to make sure we have time to estimate the offset.
- aos::TimerHandler *const quit = pi2_client_event_loop->AddTimer(
- [this]() { pi2_client_event_loop->Exit(); });
- pi2_client_event_loop->OnRun([this, quit, duration]() {
+ aos::TimerHandler *const quit =
+ client_event_loop_->AddTimer([this]() { client_event_loop_->Exit(); });
+ client_event_loop_->OnRun([this, quit, duration]() {
// Stop between timestamps, not exactly on them.
- quit->Schedule(pi2_client_event_loop->monotonic_now() + duration);
+ quit->Schedule(client_event_loop_->monotonic_now() + duration);
});
// And go!
- pi2_client_event_loop->Run();
-}
-
-void MessageBridgeParameterizedTest::StartPi2Client() {
- LOG(INFO) << "Starting pi2 client";
- pi2_client_thread =
- std::make_unique<ThreadedEventLoopRunner>(pi2_client_event_loop.get());
-}
-
-void MessageBridgeParameterizedTest::StopPi2Client() {
- LOG(INFO) << "Stopping pi2 client";
- pi2_client_thread.reset();
- pi2_message_bridge_client.reset();
- pi2_client_event_loop.reset();
-}
-
-void MessageBridgeParameterizedTest::MakePi2Test() {
- OnPi2();
- LOG(INFO) << "Making pi2 test";
- FLAGS_application_name = "test2";
- pi2_test_event_loop = std::make_unique<aos::ShmEventLoop>(&config.message());
-
- pi2_test_event_loop->MakeWatcher(
- "/pi2/aos", [](const ServerStatistics &stats) {
- VLOG(1) << "/pi2/aos ServerStatistics " << FlatbufferToJson(&stats);
- });
-
- pi2_test_event_loop->MakeWatcher(
- "/pi2/aos", [](const ClientStatistics &stats) {
- VLOG(1) << "/pi2/aos ClientStatistics " << FlatbufferToJson(&stats);
- });
-
- pi2_test_event_loop->MakeWatcher("/pi1/aos", [this](
- const Timestamp ×tamp) {
- VLOG(1) << "/pi1/aos Timestamp " << FlatbufferToJson(×tamp);
- EXPECT_EQ(pi2_test_event_loop->context().source_boot_uuid, pi1_boot_uuid_);
- });
- pi2_test_event_loop->MakeWatcher("/pi2/aos", [](const Timestamp ×tamp) {
- VLOG(1) << "/pi2/aos Timestamp " << FlatbufferToJson(×tamp);
- });
-}
-
-void MessageBridgeParameterizedTest::StartPi2Test() {
- LOG(INFO) << "Starting pi2 test";
- pi2_test_thread =
- std::make_unique<ThreadedEventLoopRunner>(pi2_test_event_loop.get());
-}
-
-void MessageBridgeParameterizedTest::StopPi2Test() {
- LOG(INFO) << "Stopping pi2 test";
- pi2_test_thread.reset();
+ client_event_loop_->Run();
}
} // namespace aos::message_bridge::testing
diff --git a/aos/network/message_bridge_test_lib.h b/aos/network/message_bridge_test_lib.h
index 047cff5..f8c0a3a 100644
--- a/aos/network/message_bridge_test_lib.h
+++ b/aos/network/message_bridge_test_lib.h
@@ -46,6 +46,45 @@
bool shared;
};
+class PiNode {
+ public:
+ PiNode(const std::string node_name, const std::string host_name,
+ const std::string app_name, const std::string config_filename);
+ // OnPi* sets the global state necessary to pretend that a ShmEventLoop is on
+ // the requisite system.
+ void OnPi();
+ void MakeServer(const std::string server_config_sha256 = "");
+ void RunServer(const chrono::nanoseconds duration);
+ void RunClient(const chrono::nanoseconds duration);
+ void StartServer();
+ void StopServer();
+ void MakeClient();
+ void StartClient();
+ void StopClient();
+ void MakeTest(const std::string test_app_name, const PiNode *other_node);
+ void StartTest();
+ void StopTest();
+
+ const UUID boot_uuid_;
+ const std::string node_name_;
+ const std::string host_name_;
+ const std::string app_name_;
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+ std::string config_sha256_;
+
+ std::unique_ptr<aos::ShmEventLoop> server_event_loop_;
+ std::unique_ptr<MessageBridgeServer> message_bridge_server_;
+ std::unique_ptr<ThreadedEventLoopRunner> server_thread_;
+
+ std::unique_ptr<aos::ShmEventLoop> client_event_loop_;
+ std::unique_ptr<MessageBridgeClient> message_bridge_client_;
+ std::unique_ptr<ThreadedEventLoopRunner> client_thread_;
+
+ std::unique_ptr<aos::ShmEventLoop> test_event_loop_;
+ std::unique_ptr<ThreadedEventLoopRunner> test_thread_;
+};
+
class MessageBridgeParameterizedTest
: public ::testing::TestWithParam<struct Param> {
protected:
@@ -53,83 +92,13 @@
bool shared() const;
- // OnPi* sets the global state necessary to pretend that a ShmEventLoop is on
- // the requisite system.
- void OnPi1();
-
- void OnPi2();
-
- void MakePi1Server(std::string server_config_sha256 = "");
-
- void RunPi1Server(chrono::nanoseconds duration);
-
- void StartPi1Server();
-
- void StopPi1Server();
-
- void MakePi1Client();
-
- void StartPi1Client();
-
- void StopPi1Client();
-
- void MakePi1Test();
-
- void StartPi1Test();
-
- void StopPi1Test();
-
- void MakePi2Server();
-
- void RunPi2Server(chrono::nanoseconds duration);
-
- void StartPi2Server();
-
- void StopPi2Server();
-
- void MakePi2Client();
-
- void RunPi2Client(chrono::nanoseconds duration);
-
- void StartPi2Client();
-
- void StopPi2Client();
-
- void MakePi2Test();
-
- void StartPi2Test();
-
- void StopPi2Test();
-
gflags::FlagSaver flag_saver_;
- aos::FlatbufferDetachedBuffer<aos::Configuration> config;
- std::string config_sha256;
+ PiNode pi1_;
+ PiNode pi2_;
- const UUID pi1_boot_uuid_;
- const UUID pi2_boot_uuid_;
-
- std::unique_ptr<aos::ShmEventLoop> pi1_server_event_loop;
- std::unique_ptr<MessageBridgeServer> pi1_message_bridge_server;
- std::unique_ptr<ThreadedEventLoopRunner> pi1_server_thread;
-
- std::unique_ptr<aos::ShmEventLoop> pi1_client_event_loop;
- std::unique_ptr<MessageBridgeClient> pi1_message_bridge_client;
- std::unique_ptr<ThreadedEventLoopRunner> pi1_client_thread;
-
- std::unique_ptr<aos::ShmEventLoop> pi1_test_event_loop;
- std::unique_ptr<ThreadedEventLoopRunner> pi1_test_thread;
-
- std::unique_ptr<aos::ShmEventLoop> pi2_server_event_loop;
- std::unique_ptr<MessageBridgeServer> pi2_message_bridge_server;
- std::unique_ptr<ThreadedEventLoopRunner> pi2_server_thread;
-
- std::unique_ptr<aos::ShmEventLoop> pi2_client_event_loop;
- std::unique_ptr<MessageBridgeClient> pi2_message_bridge_client;
- std::unique_ptr<ThreadedEventLoopRunner> pi2_client_thread;
-
- std::unique_ptr<aos::ShmEventLoop> pi2_test_event_loop;
- std::unique_ptr<ThreadedEventLoopRunner> pi2_test_thread;
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+ std::string config_sha256_;
};
} // namespace aos::message_bridge::testing
diff --git a/aos/network/message_bridge_test_mismatched_configs_pi1_and_pi2.json b/aos/network/message_bridge_test_mismatched_configs_pi1_and_pi2.json
new file mode 100644
index 0000000..bad48c8
--- /dev/null
+++ b/aos/network/message_bridge_test_mismatched_configs_pi1_and_pi2.json
@@ -0,0 +1,137 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi2",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2",
+ "type": "aos.message_bridge.RemoteMessage",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1",
+ "type": "aos.message_bridge.RemoteMessage",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1",
+ "hostname": "localhost",
+ "hostnames": ["raspberrypi"],
+ "port": 9971
+ },
+ {
+ "name": "pi2",
+ "hostname": "localhost",
+ "hostnames": ["raspberrypi2"],
+ "port": 9972
+ }
+ ]
+ }
diff --git a/aos/network/message_bridge_test_mismatched_configs_pi1_and_pi3.json b/aos/network/message_bridge_test_mismatched_configs_pi1_and_pi3.json
new file mode 100644
index 0000000..20b3f9a
--- /dev/null
+++ b/aos/network/message_bridge_test_mismatched_configs_pi1_and_pi3.json
@@ -0,0 +1,152 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi3/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi3",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi3",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi3/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi3",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi3"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi3/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi3",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi3/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi3",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi3",
+ "type": "aos.message_bridge.RemoteMessage",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi3/aos/remote_timestamps/pi1",
+ "type": "aos.message_bridge.RemoteMessage",
+ "source_node": "pi3",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi3/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi3",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi3"
+ },
+ "rename": {
+ "name": "/pi3/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1",
+ "hostname": "localhost",
+ "hostnames": ["raspberrypi"],
+ "port": 9971
+ },
+ {
+ "name": "pi2",
+ "hostname": "localhost",
+ "hostnames": ["raspberrypi2"],
+ "port": 9972
+ },
+ {
+ "name": "pi3",
+ "hostname": "localhost",
+ "hostnames": ["raspberrypi3"],
+ "port": 9973
+ }
+ ]
+}
diff --git a/aos/network/multinode_timestamp_filter.cc b/aos/network/multinode_timestamp_filter.cc
index 0a0385e..cae9e1f 100644
--- a/aos/network/multinode_timestamp_filter.cc
+++ b/aos/network/multinode_timestamp_filter.cc
@@ -82,7 +82,7 @@
}
} // namespace
-size_t NewtonSolver::solve_number_ = 0u;
+std::atomic<size_t> NewtonSolver::solve_number_ = 0u;
NewtonSolver::NewtonSolver() : my_solve_number_(solve_number_++) {}
@@ -2016,10 +2016,21 @@
CHECK_NOTNULL(filter);
const Node *node = configuration()->nodes()->Get(node_index);
+ // The remote time could be from a reliable message long ago,
+ // whereas the transmit time is the latest timestamp we have on
+ // the far side. Use that for recovering time when we have it.
+ // There are a ton of logs from before that timestamp was added.
+ // Fall back to the remote time in those.
+ const BootTimestamp monotonic_remote_time =
+ msg->monotonic_remote_transmit_time.time !=
+ monotonic_clock::min_time
+ ? msg->monotonic_remote_transmit_time
+ : msg->monotonic_remote_time;
+
// Call the correct method depending on if we are the forward or
// reverse direction here.
filter->Sample(node, msg->monotonic_event_time,
- msg->monotonic_remote_time);
+ monotonic_remote_time);
if (!node_samples_.empty()) {
const size_t sending_node_index =
@@ -2029,8 +2040,8 @@
// and monotonic_event_time was the time it was received.
node_samples_[node_index]
.nodes[sending_node_index]
- .messages.emplace(std::make_pair(
- msg->monotonic_event_time, msg->monotonic_remote_time));
+ .messages.emplace(std::make_pair(msg->monotonic_event_time,
+ monotonic_remote_time));
}
if (msg->monotonic_timestamp_time != BootTimestamp::min_time()) {
@@ -2044,7 +2055,7 @@
const size_t sending_node_index =
source_node_index_[msg->channel_index];
// The timestamp then went back from node node_index to
- // sending_node_index. monotonic_event_time is the time it
+ // sending_node_index. monotonic_transmit_time is the time it
// was sent, and monotonic_timestamp_time was the time it was
// received.
node_samples_[sending_node_index]
@@ -2933,7 +2944,8 @@
}
if (VLOG_IS_ON(1)) {
- VLOG(1) << "Candidate solution for node " << node_a_index << " is";
+ VLOG(1) << "Candidate solution for node " << node_a_index
+ << " on solve number " << solver.my_solve_number() << " is";
for (size_t i = 0; i < solution.size(); ++i) {
VLOG(1) << " " << solution[i];
}
diff --git a/aos/network/multinode_timestamp_filter.h b/aos/network/multinode_timestamp_filter.h
index 893f816..66dcbff 100644
--- a/aos/network/multinode_timestamp_filter.h
+++ b/aos/network/multinode_timestamp_filter.h
@@ -1,6 +1,7 @@
#ifndef AOS_NETWORK_MULTINODE_TIMESTAMP_FILTER_H_
#define AOS_NETWORK_MULTINODE_TIMESTAMP_FILTER_H_
+#include <atomic>
#include <functional>
#include <map>
#include <string_view>
@@ -171,7 +172,7 @@
size_t my_solve_number_;
// The global solve number counter used to deterministically find problems.
- static size_t solve_number_;
+ static std::atomic<size_t> solve_number_;
};
// A condensed representation of the time estimation problem statement. This is
diff --git a/aos/network/remote_data.fbs b/aos/network/remote_data.fbs
index 0813e57..4e4c7af 100644
--- a/aos/network/remote_data.fbs
+++ b/aos/network/remote_data.fbs
@@ -20,6 +20,12 @@
// UUID for this boot. This is 16 bytes long.
boot_uuid:[uint8] (id: 5);
+
+ // Time that the message was handed to the kernel to be published over the
+ // network on the remote node.
+ //
+ // See MessageHeader fbs definition for more details.
+ monotonic_remote_transmit_time:int64 = -9223372036854775808(id: 6);
}
root_type RemoteData;
diff --git a/aos/network/remote_message.fbs b/aos/network/remote_message.fbs
index 6d2a8d1..4305f51 100644
--- a/aos/network/remote_message.fbs
+++ b/aos/network/remote_message.fbs
@@ -32,6 +32,13 @@
// UUID for this boot.
boot_uuid:[uint8] (id: 9);
+
+ // The time that the message was transmitted on the source node to the
+ // destination node i.e handed to the kernel to be published over the
+ // message bridge.
+ //
+ // See MessageHeader fbs definition for more details.
+ monotonic_remote_transmit_time:int64 = -9223372036854775808(id: 10);
}
root_type RemoteMessage;
diff --git a/aos/network/timestamp_filter.cc b/aos/network/timestamp_filter.cc
index 914f993..f3aea62 100644
--- a/aos/network/timestamp_filter.cc
+++ b/aos/network/timestamp_filter.cc
@@ -25,6 +25,12 @@
return ss.str();
}
+std::string TimeString(const logger::BootTimestamp t, logger::BootDuration o) {
+ std::stringstream ss;
+ ss << "O(" << t << ") = " << o << ", remote " << t + o;
+ return ss.str();
+}
+
std::string TimeString(const aos::monotonic_clock::time_point t_base, double t,
std::chrono::nanoseconds o_base, double o) {
std::stringstream ss;
@@ -43,6 +49,11 @@
return TimeString(std::get<0>(t), std::get<1>(t));
}
+std::string TimeString(
+ const std::tuple<logger::BootTimestamp, logger::BootDuration> t) {
+ return TimeString(std::get<0>(t), std::get<1>(t));
+}
+
void ClippedAverageFilterPrintHeader(FILE *fp) {
fprintf(fp,
"# time_since_start, sample_ns, filtered_offset, offset, "
@@ -531,6 +542,11 @@
return std::make_pair(pointer, std::make_pair(t0, t1));
}
+ VLOG(1) << "Other points are: " << pointer.other_points_.size();
+ for (const auto &x : pointer.other_points_) {
+ VLOG(1) << " " << TimeString(x.second);
+ }
+
// The invariant of pointer is that other_points is bounded by t0, t1. Confirm
// it before we return things depending on it since it is easy.
CHECK_GT(std::get<0>(pointer.other_points_[0].second), std::get<0>(t0));
@@ -1266,36 +1282,39 @@
void NoncausalTimestampFilter::Sample(BootTimestamp monotonic_now_all,
BootDuration sample_ns) {
filter(monotonic_now_all.boot, sample_ns.boot)
- ->filter.Sample(monotonic_now_all.time, sample_ns.duration);
+ ->filter.Sample(monotonic_now_all, sample_ns);
}
void NoncausalTimestampFilter::SingleFilter::Sample(
- monotonic_clock::time_point monotonic_now, chrono::nanoseconds sample_ns) {
+ logger::BootTimestamp monotonic_now, logger::BootDuration sample_ns) {
// The first sample is easy. Just do it!
if (timestamps_.size() == 0) {
VLOG(1) << node_names_ << " Initial sample of "
<< TimeString(monotonic_now, sample_ns);
- timestamps_.emplace_back(std::make_tuple(monotonic_now, sample_ns));
+ timestamps_.emplace_back(
+ std::make_tuple(monotonic_now.time, sample_ns.duration));
CHECK(!fully_frozen_)
<< ": " << node_names_
<< " Returned a horizontal line previously and then "
"got a new sample at "
<< monotonic_now << ", "
- << chrono::duration<double>(monotonic_now - std::get<0>(timestamps_[0]))
+ << chrono::duration<double>(monotonic_now.time -
+ std::get<0>(timestamps_[0]))
.count()
<< " seconds after the last sample at " << std::get<0>(timestamps_[0])
<< ". Increase --time_estimation_buffer_seconds to greater than "
- << chrono::duration<double>(monotonic_now - std::get<0>(timestamps_[0]))
+ << chrono::duration<double>(monotonic_now.time -
+ std::get<0>(timestamps_[0]))
.count()
<< ", or set --force_timestamp_loading";
return;
}
- CHECK_GT(monotonic_now, frozen_time_)
+ CHECK_GT(monotonic_now.time, frozen_time_)
<< ": " << node_names_ << " Tried to insert " << monotonic_now
<< " before the frozen time of " << frozen_time_
<< ". Increase "
"--time_estimation_buffer_seconds to greater than "
- << chrono::duration<double>(frozen_time_ - monotonic_now).count()
+ << chrono::duration<double>(frozen_time_ - monotonic_now.time).count()
<< ", or set --force_timestamp_loading";
// Future samples get quite a bit harder. We want the line to track the
@@ -1303,12 +1322,13 @@
std::tuple<aos::monotonic_clock::time_point, chrono::nanoseconds> back =
timestamps_.back();
- aos::monotonic_clock::duration dt = monotonic_now - std::get<0>(back);
- aos::monotonic_clock::duration doffset = sample_ns - std::get<1>(back);
+ aos::monotonic_clock::duration dt = monotonic_now.time - std::get<0>(back);
+ aos::monotonic_clock::duration doffset =
+ sample_ns.duration - std::get<1>(back);
if (dt == chrono::nanoseconds(0) && doffset == chrono::nanoseconds(0)) {
VLOG(1) << node_names_ << " Duplicate sample of O(" << monotonic_now
- << ") = " << sample_ns.count() << ", remote time "
+ << ") = " << sample_ns << ", remote time "
<< monotonic_now + sample_ns;
return;
@@ -1337,11 +1357,13 @@
<< " Returned a horizontal line previously and then got a new "
"sample at "
<< monotonic_now << ", "
- << chrono::duration<double>(monotonic_now - std::get<0>(timestamps_[0]))
+ << chrono::duration<double>(monotonic_now.time -
+ std::get<0>(timestamps_[0]))
.count()
<< " seconds after the last sample at " << std::get<0>(timestamps_[0])
<< ". Increase --time_estimation_buffer_seconds to greater than "
- << chrono::duration<double>(monotonic_now - std::get<0>(timestamps_[0]))
+ << chrono::duration<double>(monotonic_now.time -
+ std::get<0>(timestamps_[0]))
.count()
<< ", or set --force_timestamp_loading";
@@ -1364,10 +1386,12 @@
<< ": " << node_names_ << " Can't pop an already frozen sample "
<< TimeString(back) << " while inserting "
<< TimeString(monotonic_now, sample_ns) << ", "
- << chrono::duration<double>(monotonic_now - std::get<0>(back)).count()
+ << chrono::duration<double>(monotonic_now.time - std::get<0>(back))
+ .count()
<< " seconds in the past. Increase --time_estimation_buffer_seconds "
"to greater than "
- << chrono::duration<double>(monotonic_now - std::get<0>(back)).count()
+ << chrono::duration<double>(monotonic_now.time - std::get<0>(back))
+ .count()
<< ", or set --force_timestamp_loading";
VLOG(1) << node_names_
<< " Removing now invalid sample during back propegation of "
@@ -1375,13 +1399,14 @@
timestamps_.pop_back();
back = timestamps_.back();
- dt = monotonic_now - std::get<0>(back);
- doffset = sample_ns - std::get<1>(back);
+ dt = monotonic_now.time - std::get<0>(back);
+ doffset = sample_ns.duration - std::get<1>(back);
}
VLOG(1) << node_names_ << " Added sample of "
<< TimeString(monotonic_now, sample_ns);
- timestamps_.emplace_back(std::make_tuple(monotonic_now, sample_ns));
+ timestamps_.emplace_back(
+ std::make_tuple(monotonic_now.time, sample_ns.duration));
return;
}
@@ -1389,7 +1414,7 @@
// point. lower_bound returns the element which we are supposed to insert
// "before".
auto it = std::lower_bound(
- timestamps_.begin(), timestamps_.end(), monotonic_now,
+ timestamps_.begin(), timestamps_.end(), monotonic_now.time,
[](const std::tuple<aos::monotonic_clock::time_point,
std::chrono::nanoseconds>
x,
@@ -1403,9 +1428,9 @@
if (it == timestamps_.begin()) {
// We are being asked to add at the beginning.
{
- const chrono::nanoseconds dt = std::get<0>(*it) - monotonic_now;
+ const chrono::nanoseconds dt = std::get<0>(*it) - monotonic_now.time;
const chrono::nanoseconds original_offset = std::get<1>(*it);
- const chrono::nanoseconds doffset = original_offset - sample_ns;
+ const chrono::nanoseconds doffset = original_offset - sample_ns.duration;
if (dt == chrono::nanoseconds(0) && doffset >= chrono::nanoseconds(0)) {
VLOG(1) << node_names_ << " Redundant timestamp "
@@ -1418,15 +1443,18 @@
VLOG(1) << node_names_ << " Added sample at beginning "
<< TimeString(monotonic_now, sample_ns);
- timestamps_.insert(it, std::make_tuple(monotonic_now, sample_ns));
+ timestamps_.insert(it,
+ std::make_tuple(monotonic_now.time, sample_ns.duration));
while (true) {
// First point was too positive, so we need to remove points after it
// until we are valid.
auto second = timestamps_.begin() + 1;
if (second != timestamps_.end()) {
- const chrono::nanoseconds dt = std::get<0>(*second) - monotonic_now;
- const chrono::nanoseconds doffset = std::get<1>(*second) - sample_ns;
+ const chrono::nanoseconds dt =
+ std::get<0>(*second) - monotonic_now.time;
+ const chrono::nanoseconds doffset =
+ std::get<1>(*second) - sample_ns.duration;
if (absl::int128(doffset.count()) *
absl::int128(MaxVelocityRatio::den) <
@@ -1477,10 +1505,12 @@
<< " < " << monotonic_now << " < " << std::get<0>(*it);
{
- chrono::nanoseconds prior_dt = monotonic_now - std::get<0>(*(it - 1));
- chrono::nanoseconds prior_doffset = sample_ns - std::get<1>(*(it - 1));
- chrono::nanoseconds next_dt = std::get<0>(*it) - monotonic_now;
- chrono::nanoseconds next_doffset = std::get<1>(*it) - sample_ns;
+ chrono::nanoseconds prior_dt =
+ monotonic_now.time - std::get<0>(*(it - 1));
+ chrono::nanoseconds prior_doffset =
+ sample_ns.duration - std::get<1>(*(it - 1));
+ chrono::nanoseconds next_dt = std::get<0>(*it) - monotonic_now.time;
+ chrono::nanoseconds next_doffset = std::get<1>(*it) - sample_ns.duration;
// If we are worse than either the previous or next point, discard.
if (absl::int128(prior_doffset.count()) *
@@ -1517,8 +1547,8 @@
// Now, insert and start propagating forwards and backwards anything we've
// made invalid. Do this simultaneously so we keep discovering anything
// new.
- auto middle_it =
- timestamps_.insert(it, std::make_tuple(monotonic_now, sample_ns));
+ auto middle_it = timestamps_.insert(
+ it, std::make_tuple(monotonic_now.time, sample_ns.duration));
VLOG(1) << node_names_ << " Inserted " << TimeString(*middle_it);
while (middle_it != timestamps_.end() && middle_it != timestamps_.begin()) {
@@ -1602,20 +1632,16 @@
removed = true;
}
- if (timestamps_size == 2) {
+ if (timestamps_size <= 2) {
if (pop_filter_ + 1u >= filters_.size()) {
return removed;
}
- // There is 1 more filter, see if there is enough data in it to switch
- // over to it.
- if (filters_[pop_filter_ + 1]->filter.timestamps_size() < 2u) {
- return removed;
- }
if (time <
- BootTimestamp{.boot = static_cast<size_t>(boot_filter->boot.first),
+ BootTimestamp{.boot = static_cast<size_t>(
+ filters_[pop_filter_ + 1]->boot.first),
.time = std::get<0>(
- filters_[pop_filter_ + 1]->filter.timestamp(1))}) {
+ filters_[pop_filter_ + 1]->filter.timestamp(0))}) {
return removed;
}
}
@@ -1661,16 +1687,80 @@
return next_to_consume_ + 1 < timestamps_.size();
}
+std::optional<std::tuple<logger::BootTimestamp, logger::BootDuration>>
+NoncausalTimestampFilter::Observe() const {
+ if (filters_.size() == 0u) {
+ return std::nullopt;
+ }
+
+ size_t current_filter = std::max(static_cast<ssize_t>(0), current_filter_);
+ while (true) {
+ const BootFilter &filter = *filters_[current_filter];
+ std::optional<
+ std::tuple<monotonic_clock::time_point, std::chrono::nanoseconds>>
+ result = filter.filter.Observe();
+ if (!result) {
+ if (current_filter + 1 == filters_.size()) {
+ return std::nullopt;
+ } else {
+ ++current_filter;
+ continue;
+ }
+ }
+ auto final_result = std::make_tuple(
+ logger::BootTimestamp{static_cast<size_t>(filter.boot.first),
+ std::get<0>(*result)},
+ logger::BootDuration{static_cast<size_t>(filter.boot.second),
+ std::get<1>(*result)});
+ VLOG(1) << NodeNames() << " Observed sample of "
+ << TimeString(final_result);
+ return final_result;
+ }
+}
+
std::optional<std::tuple<monotonic_clock::time_point, std::chrono::nanoseconds>>
NoncausalTimestampFilter::SingleFilter::Observe() const {
if (timestamps_.empty() || next_to_consume_ >= timestamps_.size()) {
return std::nullopt;
}
- VLOG(1) << node_names_ << " Observed sample of "
+ VLOG(2) << node_names_ << " Observed sample of "
<< TimeString(timestamp(next_to_consume_));
return timestamp(next_to_consume_);
}
+std::optional<std::tuple<logger::BootTimestamp, logger::BootDuration>>
+NoncausalTimestampFilter::Consume() {
+ if (filters_.size() == 0u) {
+ return std::nullopt;
+ }
+ DCHECK_LT(current_filter_, static_cast<ssize_t>(filters_.size()));
+
+ while (true) {
+ std::optional<
+ std::tuple<monotonic_clock::time_point, std::chrono::nanoseconds>>
+ result =
+ current_filter_ < 0 ? std::nullopt
+ : filters_[current_filter_]->filter.Consume();
+ if (!result) {
+ if (static_cast<size_t>(current_filter_ + 1) == filters_.size()) {
+ return std::nullopt;
+ } else {
+ ++current_filter_;
+ continue;
+ }
+ }
+ BootFilter &filter = *filters_[current_filter_];
+ auto final_result = std::make_tuple(
+ logger::BootTimestamp{static_cast<size_t>(filter.boot.first),
+ std::get<0>(*result)},
+ logger::BootDuration{static_cast<size_t>(filter.boot.second),
+ std::get<1>(*result)});
+ VLOG(1) << NodeNames() << " Consumed sample of "
+ << TimeString(final_result);
+ return final_result;
+ }
+}
+
std::optional<std::tuple<monotonic_clock::time_point, std::chrono::nanoseconds>>
NoncausalTimestampFilter::SingleFilter::Consume() {
if (timestamps_.empty() || next_to_consume_ >= timestamps_.size()) {
@@ -1678,7 +1768,7 @@
}
auto result = timestamp(next_to_consume_);
- VLOG(1) << node_names_ << " Consumed sample of " << TimeString(result);
+ VLOG(2) << node_names_ << " Consumed sample of " << TimeString(result);
++next_to_consume_;
return result;
}
diff --git a/aos/network/timestamp_filter.h b/aos/network/timestamp_filter.h
index be9318e..2361797 100644
--- a/aos/network/timestamp_filter.h
+++ b/aos/network/timestamp_filter.h
@@ -469,63 +469,11 @@
// because solving for them doesn't add any additional value. We will already
// be solving the other direction.
std::optional<std::tuple<logger::BootTimestamp, logger::BootDuration>>
- Observe() const {
- if (filters_.size() == 0u) {
- return std::nullopt;
- }
-
- size_t current_filter = std::max(static_cast<ssize_t>(0), current_filter_);
- while (true) {
- const BootFilter &filter = *filters_[current_filter];
- std::optional<
- std::tuple<monotonic_clock::time_point, std::chrono::nanoseconds>>
- result = filter.filter.Observe();
- if (!result) {
- if (current_filter + 1 == filters_.size()) {
- return std::nullopt;
- } else {
- ++current_filter;
- continue;
- }
- }
- return std::make_tuple(
- logger::BootTimestamp{static_cast<size_t>(filter.boot.first),
- std::get<0>(*result)},
- logger::BootDuration{static_cast<size_t>(filter.boot.second),
- std::get<1>(*result)});
- }
- }
+ Observe() const;
// Returns the next timestamp in the queue if available, incrementing the
// pointer.
std::optional<std::tuple<logger::BootTimestamp, logger::BootDuration>>
- Consume() {
- if (filters_.size() == 0u) {
- return std::nullopt;
- }
- DCHECK_LT(current_filter_, static_cast<ssize_t>(filters_.size()));
-
- while (true) {
- std::optional<
- std::tuple<monotonic_clock::time_point, std::chrono::nanoseconds>>
- result =
- current_filter_ < 0 ? std::nullopt
- : filters_[current_filter_]->filter.Consume();
- if (!result) {
- if (static_cast<size_t>(current_filter_ + 1) == filters_.size()) {
- return std::nullopt;
- } else {
- ++current_filter_;
- continue;
- }
- }
- BootFilter &filter = *filters_[current_filter_];
- return std::make_tuple(
- logger::BootTimestamp{static_cast<size_t>(filter.boot.first),
- std::get<0>(*result)},
- logger::BootDuration{static_cast<size_t>(filter.boot.second),
- std::get<1>(*result)});
- }
- }
+ Consume();
// Public for testing.
// Assuming that there are at least 2 points in timestamps_, finds the 2
@@ -736,8 +684,8 @@
aos::monotonic_clock::time_point tb_base, double tb,
bool validate_popped, bool quiet) const;
- void Sample(monotonic_clock::time_point monotonic_now,
- std::chrono::nanoseconds sample_ns);
+ void Sample(logger::BootTimestamp monotonic_now,
+ logger::BootDuration sample_ns);
private:
std::string node_names_;
diff --git a/aos/network/timestamp_filter_test.cc b/aos/network/timestamp_filter_test.cc
index f3b7290..0c37ed5 100644
--- a/aos/network/timestamp_filter_test.cc
+++ b/aos/network/timestamp_filter_test.cc
@@ -797,9 +797,10 @@
filter.FreezeUntil(tb, {0, monotonic_clock::min_time});
EXPECT_DEATH({ filter.Sample(tb, oa); },
- "monotonic_now > frozen_time_ \\(0.100000000sec vs. "
+ "monotonic_now.time > frozen_time_ \\(0.100000000sec vs. "
"0.100000000sec\\) : test_a -> test_b Tried to insert "
- "0.100000000sec before the frozen time of 0.100000000sec. "
+ "\\{.boot=0, .time=0.100000000sec\\} before the frozen time "
+ "of 0.100000000sec. "
"Increase --time_estimation_buffer_seconds to greater than 0");
}
@@ -816,7 +817,8 @@
{ filter.Sample(tc, oc); },
"test_a -> test_b Returned a horizontal line previously and then got a "
"new sample at "
- "0.200000000sec, 0.2 seconds after the last sample at 0.000000000sec");
+ "\\{.boot=0, .time=0.200000000sec\\}, 0.2 seconds after the last "
+ "sample at 0.000000000sec");
}
{
@@ -830,9 +832,10 @@
EXPECT_DEATH(
{ filter.Sample(tb, ob); },
- "monotonic_now > frozen_time_ \\(0.100000000sec vs. "
+ "monotonic_now.time > frozen_time_ \\(0.100000000sec vs. "
"0.200000000sec\\) : test_a -> test_b Tried to insert "
- "0.100000000sec before the frozen time of 0.200000000sec. "
+ "\\{.boot=0, .time=0.100000000sec\\} before the frozen time of "
+ "0.200000000sec. "
"Increase --time_estimation_buffer_seconds to greater than 0.1");
}
@@ -848,9 +851,10 @@
filter.FreezeUntil(tb, {0, monotonic_clock::min_time});
EXPECT_DEATH({ filter.Sample(tb, oa); },
- "monotonic_now > frozen_time_ \\(0.100000000sec vs. "
+ "monotonic_now.time > frozen_time_ \\(0.100000000sec vs. "
"0.100000000sec\\) : test_a -> test_b Tried to insert "
- "0.100000000sec before the frozen time of 0.100000000sec. "
+ "\\{.boot=0, .time=0.100000000sec\\} before the frozen time "
+ "of 0.100000000sec. "
"Increase --time_estimation_buffer_seconds to greater than 0");
EXPECT_DEATH({ filter.Sample(tb + chrono::nanoseconds(1), oa); },
"test_a -> test_b Can't pop an already frozen sample");
diff --git a/aos/realtime.cc b/aos/realtime.cc
index 72daa6b..1e10457 100644
--- a/aos/realtime.cc
+++ b/aos/realtime.cc
@@ -161,8 +161,24 @@
MarkRealtime(false);
}
+std::ostream &operator<<(std::ostream &stream, const cpu_set_t &cpuset) {
+ stream << "{CPUs ";
+ bool first_found = false;
+ for (int i = 0; i < CPU_SETSIZE; ++i) {
+ if (CPU_ISSET(i, &cpuset)) {
+ if (first_found) {
+ stream << ", ";
+ }
+ stream << i;
+ first_found = true;
+ }
+ }
+ stream << "}";
+ return stream;
+}
+
void SetCurrentThreadAffinity(const cpu_set_t &cpuset) {
- PCHECK(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0);
+ PCHECK(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0) << cpuset;
}
void SetCurrentThreadName(const std::string_view name) {
diff --git a/aos/realtime.h b/aos/realtime.h
index caf0daa..261eb26 100644
--- a/aos/realtime.h
+++ b/aos/realtime.h
@@ -3,6 +3,7 @@
#include <sched.h>
+#include <ostream>
#include <string_view>
#include "glog/logging.h"
@@ -28,6 +29,9 @@
// name can have a maximum of 16 characters.
void SetCurrentThreadName(const std::string_view name);
+// Stringifies the cpu_set_t for streams.
+std::ostream &operator<<(std::ostream &stream, const cpu_set_t &cpuset);
+
// Creates a cpu_set_t from a list of CPUs.
inline cpu_set_t MakeCpusetFromCpus(std::initializer_list<int> cpus) {
cpu_set_t result;
diff --git a/aos/realtime_test.cc b/aos/realtime_test.cc
index 348489b..9a0c94e 100644
--- a/aos/realtime_test.cc
+++ b/aos/realtime_test.cc
@@ -132,6 +132,20 @@
#endif
+// Tests that we see which CPUs we tried to set when it fails. This can be
+// useful for debugging.
+TEST(RealtimeDeathTest, SetAffinityErrorMessage) {
+ EXPECT_DEATH({ SetCurrentThreadAffinity(MakeCpusetFromCpus({1000})); },
+ "sched_setaffinity\\(0, sizeof\\(cpuset\\), &cpuset\\) == 0 "
+ "\\{CPUs 1000\\}: Invalid argument");
+ EXPECT_DEATH(
+ {
+ SetCurrentThreadAffinity(MakeCpusetFromCpus({1000, 1001}));
+ },
+ "sched_setaffinity\\(0, sizeof\\(cpuset\\), &cpuset\\) == 0 "
+ "\\{CPUs 1000, 1001\\}: Invalid argument");
+}
+
} // namespace aos::testing
// We need a special gtest main to force die_on_malloc support on. Otherwise
diff --git a/aos/starter/starter_rpc_lib.cc b/aos/starter/starter_rpc_lib.cc
index 891355e..754fc2c 100644
--- a/aos/starter/starter_rpc_lib.cc
+++ b/aos/starter/starter_rpc_lib.cc
@@ -63,6 +63,7 @@
: event_loop_(event_loop),
timeout_timer_(event_loop_->AddTimer([this]() { Timeout(); })),
cmd_sender_(event_loop_->MakeSender<StarterRpc>("/aos")) {
+ timeout_timer_->set_name("rpc_timeout");
if (configuration::MultiNode(event_loop_->configuration())) {
for (const aos::Node *node :
configuration::GetNodes(event_loop_->configuration())) {
diff --git a/aos/starter/starter_rpc_lib.h b/aos/starter/starter_rpc_lib.h
index ae2450c..a949928 100644
--- a/aos/starter/starter_rpc_lib.h
+++ b/aos/starter/starter_rpc_lib.h
@@ -37,8 +37,11 @@
timeout_handler_ = handler;
}
+ // Sets the callback to be called on success. Note: this isn't safe to call
+ // while the previous success handler is running unless you use std::ref to
+ // manage its lifetime some other way.
void SetSuccessHandler(std::function<void()> handler) {
- success_handler_ = handler;
+ success_handler_ = std::move(handler);
}
private:
diff --git a/aos/starter/starter_test.cc b/aos/starter/starter_test.cc
index d26f414..5888233 100644
--- a/aos/starter/starter_test.cc
+++ b/aos/starter/starter_test.cc
@@ -535,7 +535,7 @@
LOG(INFO) << "Waiting for starter to close.";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
- client.SetTimeoutHandler(stage3);
+ client.SetTimeoutHandler(std::ref(stage3));
client.SetSuccessHandler([]() {
LOG(INFO) << "stage3 success handler called.";
FAIL() << ": Command should not have succeeded here.";
@@ -549,7 +549,7 @@
LOG(INFO) << "Begin stage1";
client.SetTimeoutHandler(
[]() { FAIL() << ": Command should not have timed out."; });
- client.SetSuccessHandler(stage2);
+ client.SetSuccessHandler(std::ref(stage2));
client.SendCommands({{Command::STOP, "ping", {client_node}}},
std::chrono::seconds(5));
LOG(INFO) << "End stage1";
diff --git a/aos/starter/starterd_lib.cc b/aos/starter/starterd_lib.cc
index 38d519d..6a1d3ed 100644
--- a/aos/starter/starterd_lib.cc
+++ b/aos/starter/starterd_lib.cc
@@ -58,6 +58,8 @@
top_(&event_loop_) {
event_loop_.SkipAosLog();
+ cleanup_timer_->set_name("cleanup");
+
if (!aos::configuration::MultiNode(config_msg_)) {
event_loop_.MakeWatcher(
"/aos",
diff --git a/aos/starter/subprocess.cc b/aos/starter/subprocess.cc
index d677922..07057a0 100644
--- a/aos/starter/subprocess.cc
+++ b/aos/starter/subprocess.cc
@@ -237,9 +237,16 @@
event_loop_->AddTimer([this]() { MaybeHandleSignal(); })),
on_change_({on_change}),
quiet_flag_(quiet_flag) {
+ // Keep the length of the timer name bounded to some reasonable length.
+ start_timer_->set_name(absl::StrCat("app_start_", name.substr(0, 10)));
+ restart_timer_->set_name(absl::StrCat("app_restart_", name.substr(0, 10)));
+ stop_timer_->set_name(absl::StrCat("app_stop_", name.substr(0, 10)));
+ pipe_timer_->set_name(absl::StrCat("app_pipe_", name.substr(0, 10)));
+ child_status_handler_->set_name(
+ absl::StrCat("app_status_handler_", name.substr(0, 10)));
// Every second poll to check if the child is dead. This is used as a
- // default for the case where the user is not directly catching SIGCHLD and
- // calling MaybeHandleSignal for us.
+ // default for the case where the user is not directly catching SIGCHLD
+ // and calling MaybeHandleSignal for us.
child_status_handler_->Schedule(event_loop_->monotonic_now(),
std::chrono::seconds(1));
}
diff --git a/tools/python/mirror_pip_packages.py b/tools/python/mirror_pip_packages.py
index 76a33a8..0581054 100644
--- a/tools/python/mirror_pip_packages.py
+++ b/tools/python/mirror_pip_packages.py
@@ -20,7 +20,7 @@
import requests
from pkginfo import Wheel
-PLAT = "manylinux_2_31"
+PLAT = "manylinux_2_34"
ARCH = "x86_64"
WHEELHOUSE_MIRROR_URL = "https://software.frc971.org/Build-Dependencies/wheelhouse"
PY_DEPS_WWWW_DIR = "/var/www/html/files/frc971/Build-Dependencies/wheelhouse"
diff --git a/tools/rust/defs.bzl b/tools/rust/defs.bzl
index 06f1ad1..0093184 100644
--- a/tools/rust/defs.bzl
+++ b/tools/rust/defs.bzl
@@ -8,12 +8,13 @@
_rust_test = "rust_test",
)
-def rust_doc_test(target_compatible_with = ["//tools/platforms/rust:has_support"], tags = [], **kwargs):
+def rust_doc_test(tags = [], **kwargs):
# TODO(james): Attempting to execute this remotely results
# in complaints about overly large files.
_rust_doc_test(
tags = tags + ["no-remote-exec"],
- target_compatible_with = target_compatible_with,
+ # TODO(adam.snaider): Investigate why doctests only work on x86_64.
+ target_compatible_with = ["@platforms//cpu:x86_64"],
**kwargs
)