Refactor ErrorCounter out from timing report code
This is helpful to allow other applications to track error counts in a
similar manner.
Change-Id: Ifc7127578c08757febc6acdc3a79e42ad7b7cce5
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/util/BUILD b/aos/util/BUILD
index 0808062..5a87251 100644
--- a/aos/util/BUILD
+++ b/aos/util/BUILD
@@ -54,6 +54,28 @@
)
cc_library(
+ name = "error_counter",
+ hdrs = ["error_counter.h"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ "@com_github_google_flatbuffers//:flatbuffers",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
+cc_test(
+ name = "error_counter_test",
+ srcs = ["error_counter_test.cc"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":error_counter",
+ "//aos:flatbuffers",
+ "//aos/events:event_loop_fbs",
+ "//aos/testing:googletest",
+ ],
+)
+
+cc_library(
name = "mcap_logger",
srcs = ["mcap_logger.cc"],
hdrs = ["mcap_logger.h"],
diff --git a/aos/util/error_counter.h b/aos/util/error_counter.h
new file mode 100644
index 0000000..cf6cb7f
--- /dev/null
+++ b/aos/util/error_counter.h
@@ -0,0 +1,71 @@
+#ifndef AOS_UTIL_ERROR_COUNTER_H_
+#define AOS_UTIL_ERROR_COUNTER_H_
+#include "flatbuffers/flatbuffers.h"
+#include "glog/logging.h"
+
+namespace aos::util {
+// Class to manage simple error counters for flatbuffer status message.
+// This presumes that you have a flatbuffer enum type Error which has
+// enum values that are continuous and start at zero. These are then
+// counted by a Count flatbuffer table that is of the format:
+// table Count {
+// error:Error (id: 0);
+// count:uint (id: 1);
+// }
+// And which is stored as a vector in the resulting status message,
+// where the index within the vector corresponds with the underlying
+// value of the enum.
+template <typename Error, typename Count>
+class ErrorCounter {
+ public:
+ static constexpr size_t kNumErrors =
+ static_cast<int>(Error::MAX) - static_cast<int>(Error::MIN) + 1;
+ static_assert(0 == static_cast<int>(Error::MIN),
+ "Expected Error enum values to start at zero.");
+ // TODO(james): Is there any good way to check that the values are contiguous?
+ // There's no Error::COUNT, and the method I previously used (checking the
+ // size of the return type of EnumValues*()) requires the user to pass that
+ // method as a template argument.
+ ErrorCounter() = default;
+ static flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
+ Initialize(flatbuffers::FlatBufferBuilder *fbb) {
+ std::array<flatbuffers::Offset<Count>, kNumErrors> count_offsets;
+ for (size_t ii = 0; ii < kNumErrors; ++ii) {
+ typename Count::Builder builder(*fbb);
+ builder.add_error(static_cast<Error>(ii));
+ builder.add_count(0);
+ count_offsets[ii] = builder.Finish();
+ }
+ const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
+ offset = fbb->CreateVector(count_offsets.data(), count_offsets.size());
+ return offset;
+ }
+
+ void set_mutable_vector(
+ flatbuffers::Vector<flatbuffers::Offset<Count>> *vector) {
+ vector_ = vector;
+ }
+
+ void InvalidateBuffer() { vector_ = nullptr; }
+
+ void IncrementError(Error error) {
+ CHECK_NOTNULL(vector_);
+ DCHECK_LT(static_cast<size_t>(error), vector_->size());
+ Count *counter = vector_->GetMutableObject(static_cast<size_t>(error));
+ counter->mutate_count(counter->count() + 1);
+ }
+
+ // Sets all the error counts to zero.
+ void ResetCounts() {
+ CHECK_NOTNULL(vector_);
+ DCHECK_EQ(vector_->size(), kNumErrors) << this << " vector " << vector_;
+ for (size_t ii = 0; ii < kNumErrors; ++ii) {
+ vector_->GetMutableObject(ii)->mutate_count(0);
+ }
+ }
+
+ private:
+ flatbuffers::Vector<flatbuffers::Offset<Count>> *vector_ = nullptr;
+};
+} // namespace aos::util
+#endif // AOS_UTIL_ERROR_COUNTER_H_
diff --git a/aos/util/error_counter_test.cc b/aos/util/error_counter_test.cc
new file mode 100644
index 0000000..2166cea
--- /dev/null
+++ b/aos/util/error_counter_test.cc
@@ -0,0 +1,37 @@
+#include "aos/util/error_counter.h"
+
+#include "aos/events/event_loop_generated.h"
+#include "aos/flatbuffers.h"
+#include "gtest/gtest.h"
+
+namespace aos::util::testing {
+// Exercises the basic API for the ErrorCounter class, ensuring that everything
+// works in the normal case.
+TEST(ErrorCounterTest, ErrorCounter) {
+ ErrorCounter<aos::timing::SendError, aos::timing::SendErrorCount> counter;
+ flatbuffers::FlatBufferBuilder fbb;
+ fbb.ForceDefaults(true);
+ const flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<aos::timing::SendErrorCount>>>
+ counts_offset = counter.Initialize(&fbb);
+ aos::timing::Sender::Builder builder(fbb);
+ builder.add_error_counts(counts_offset);
+ fbb.Finish(builder.Finish());
+ aos::FlatbufferDetachedBuffer<aos::timing::Sender> message = fbb.Release();
+ counter.set_mutable_vector(message.mutable_message()->mutable_error_counts());
+ counter.IncrementError(aos::timing::SendError::MESSAGE_SENT_TOO_FAST);
+ counter.IncrementError(aos::timing::SendError::MESSAGE_SENT_TOO_FAST);
+ counter.IncrementError(aos::timing::SendError::INVALID_REDZONE);
+ ASSERT_EQ(2u, message.message().error_counts()->size());
+ EXPECT_EQ(aos::timing::SendError::MESSAGE_SENT_TOO_FAST,
+ message.message().error_counts()->Get(0)->error());
+ EXPECT_EQ(2u, message.message().error_counts()->Get(0)->count());
+ EXPECT_EQ(aos::timing::SendError::INVALID_REDZONE,
+ message.message().error_counts()->Get(1)->error());
+ EXPECT_EQ(1u, message.message().error_counts()->Get(1)->count());
+
+ counter.ResetCounts();
+ EXPECT_EQ(0u, message.message().error_counts()->Get(0)->count());
+ EXPECT_EQ(0u, message.message().error_counts()->Get(1)->count());
+}
+} // namespace aos::util::testing