traced_perf: unwind queue type

In the perf profiler, there's a need for samples to be enqueued between
the reader, and the unwinder. There will be a single writer thread, and
a (separate) single reader thread.

The queue is implemented as a basic ring buffer of an arbitrary type
(std::array<T, N> + atomic rd/wr pointers). The implementation is
generic, but I've kept the type with a dedicated name (UnwindQueue)
until we find a need to reuse it.

Likewise, the read and write primitives are skewed towards the expected
use-case: writer commits entries one by one, reader can consume a batch
of contiguous entries.

See follow-up patch for more context.

Bug: 144281346
Change-Id: I83d7d6100f5cf52fa91c93d6c75624af6b788b7e
diff --git a/Android.bp b/Android.bp
index 6c873f7..6f56224 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5948,6 +5948,7 @@
   name: "perfetto_src_profiling_perf_producer_unittests",
   srcs: [
     "src/profiling/perf/event_config_unittest.cc",
+    "src/profiling/perf/unwind_queue_unittest.cc",
   ],
 }
 
@@ -5967,6 +5968,11 @@
   ],
 }
 
+// GN: //src/profiling/perf:unwinding
+filegroup {
+  name: "perfetto_src_profiling_perf_unwinding",
+}
+
 // GN: //src/profiling/symbolizer:symbolize_database
 filegroup {
   name: "perfetto_src_profiling_symbolizer_symbolize_database",
@@ -7237,6 +7243,7 @@
     ":perfetto_src_profiling_perf_producer",
     ":perfetto_src_profiling_perf_producer_unittests",
     ":perfetto_src_profiling_perf_regs_parsing",
+    ":perfetto_src_profiling_perf_unwinding",
     ":perfetto_src_profiling_unittests",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_protozero_testing_messages_cpp_gen",
diff --git a/src/profiling/perf/BUILD.gn b/src/profiling/perf/BUILD.gn
index 0d9a916..dcf57d7 100644
--- a/src/profiling/perf/BUILD.gn
+++ b/src/profiling/perf/BUILD.gn
@@ -96,10 +96,19 @@
   ]
 }
 
+source_set("unwinding") {
+  deps = [
+    "../../../gn:default_deps",
+    "../../../src/base",
+  ]
+  sources = [ "unwind_queue.h" ]
+}
+
 source_set("producer_unittests") {
   testonly = true
   deps = [
     ":producer",
+    ":unwinding",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
     "../../../protos/perfetto/config:cpp",
@@ -109,5 +118,8 @@
     "../../../src/protozero",
     "../../base",
   ]
-  sources = [ "event_config_unittest.cc" ]
+  sources = [
+    "event_config_unittest.cc",
+    "unwind_queue_unittest.cc",
+  ]
 }
diff --git a/src/profiling/perf/unwind_queue.h b/src/profiling/perf/unwind_queue.h
new file mode 100644
index 0000000..8231251
--- /dev/null
+++ b/src/profiling/perf/unwind_queue.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_PERF_UNWIND_QUEUE_H_
+#define SRC_PROFILING_PERF_UNWIND_QUEUE_H_
+
+#include <array>
+#include <atomic>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace profiling {
+
+struct WriteView {
+  bool valid;  // if false: buffer is full, and |write_pos| is invalid
+  uint64_t write_pos;
+};
+
+struct ReadView {
+  uint64_t read_pos;
+  uint64_t write_pos;
+};
+
+// Single-writer, single-reader ring buffer of fixed-size entries (of any
+// default-constructible type). Size of the buffer is static for the lifetime of
+// UnwindQueue, and must be a power of two.
+// Writer side appends entries once at a time, and must stop if there
+// is no available capacity.
+// Reader side sees all unconsumed entries, and can advance the reader position
+// by any amount.
+template <typename T, uint32_t QueueSize>
+class UnwindQueue {
+ public:
+  UnwindQueue() {
+    static_assert(QueueSize != 0 && ((QueueSize & (QueueSize - 1)) == 0),
+                  "not a power of two");
+  }
+
+  UnwindQueue(const UnwindQueue&) = delete;
+  UnwindQueue& operator=(const UnwindQueue&) = delete;
+  UnwindQueue(UnwindQueue&&) = delete;
+  UnwindQueue& operator=(UnwindQueue&&) = delete;
+
+  T& at(uint64_t pos) { return data_[pos % QueueSize]; }
+
+  WriteView BeginWrite() {
+    uint64_t rd = rd_pos_.load(std::memory_order_acquire);
+    uint64_t wr = wr_pos_.load(std::memory_order_relaxed);
+
+    PERFETTO_DCHECK(wr >= rd);
+    if (wr - rd >= QueueSize)
+      return WriteView{false, 0};  // buffer fully occupied
+
+    return WriteView{true, wr};
+  }
+
+  void CommitWrite() { wr_pos_.fetch_add(1u, std::memory_order_release); }
+
+  ReadView BeginRead() {
+    uint64_t wr = wr_pos_.load(std::memory_order_acquire);
+    uint64_t rd = rd_pos_.load(std::memory_order_relaxed);
+
+    PERFETTO_DCHECK(wr >= rd && wr - rd <= QueueSize);
+    return ReadView{rd, wr};
+  }
+
+  void CommitNewReadPosition(uint64_t pos) {
+    rd_pos_.store(pos, std::memory_order_release);
+  }
+
+ private:
+  std::array<T, QueueSize> data_;
+  std::atomic<uint64_t> wr_pos_{0};
+  std::atomic<uint64_t> rd_pos_{0};
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_UNWIND_QUEUE_H_
diff --git a/src/profiling/perf/unwind_queue_unittest.cc b/src/profiling/perf/unwind_queue_unittest.cc
new file mode 100644
index 0000000..e906d59
--- /dev/null
+++ b/src/profiling/perf/unwind_queue_unittest.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/perf/unwind_queue.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+TEST(UnwindQueueTest, SinglePass) {
+  static constexpr uint32_t kCapacity = 4;
+  UnwindQueue<int, kCapacity> queue;
+
+  // write kCapacity entries
+  for (int i = 0; i < static_cast<int>(kCapacity); i++) {
+    WriteView v = queue.BeginWrite();
+    ASSERT_TRUE(v.valid);
+    queue.at(v.write_pos) = i;
+    queue.CommitWrite();
+  }
+  {
+    // no more available capacity
+    WriteView v = queue.BeginWrite();
+    ASSERT_FALSE(v.valid);
+  }
+
+  // reader sees all four writes
+  ReadView v = queue.BeginRead();
+  ASSERT_EQ(v.read_pos, 0u);
+  ASSERT_EQ(v.write_pos, 4u);
+
+  std::vector<int> read_back;
+  for (auto pos = v.read_pos; pos < v.write_pos; pos++) {
+    read_back.push_back(queue.at(pos));
+  }
+  queue.CommitNewReadPosition(v.write_pos);
+
+  ASSERT_THAT(read_back, ::testing::ElementsAre(0, 1, 2, 3));
+
+  // writer sees an available slot
+  ASSERT_TRUE(queue.BeginWrite().valid);
+  // reader caught up
+  ASSERT_TRUE(queue.BeginRead().read_pos == queue.BeginRead().write_pos);
+}
+
+TEST(UnwindQueueTest, Wrapped) {
+  static constexpr uint32_t kCapacity = 4;
+  UnwindQueue<int, kCapacity> queue;
+
+  // write kCapacity entries
+  for (int i = 0; i < static_cast<int>(kCapacity); i++) {
+    WriteView v = queue.BeginWrite();
+    ASSERT_TRUE(v.valid);
+    queue.at(v.write_pos) = i;
+    queue.CommitWrite();
+  }
+
+  // no more available capacity
+  ASSERT_FALSE(queue.BeginWrite().valid);
+
+  {
+    // consume 2 entries (partial read)
+    ReadView v = queue.BeginRead();
+    ASSERT_EQ(v.read_pos, 0u);
+    ASSERT_EQ(v.write_pos, 4u);
+    queue.CommitNewReadPosition(v.read_pos + 2);
+  }
+
+  // write 2 more entries
+  for (int i = 0; i < 2; i++) {
+    WriteView v = queue.BeginWrite();
+    ASSERT_TRUE(v.valid);
+    queue.at(v.write_pos) = 4 + i;
+    queue.CommitWrite();
+  }
+
+  // no more available capacity
+  ASSERT_FALSE(queue.BeginWrite().valid);
+
+  // read the remainder of the buffer
+  ReadView v = queue.BeginRead();
+  ASSERT_EQ(v.read_pos, 2u);
+  ASSERT_EQ(v.write_pos, 6u);
+
+  std::vector<int> read_back;
+  for (auto pos = v.read_pos; pos < v.write_pos; pos++) {
+    read_back.push_back(queue.at(pos));
+  }
+  queue.CommitNewReadPosition(v.write_pos);
+
+  ASSERT_THAT(read_back, ::testing::ElementsAre(2, 3, 4, 5));
+
+  // writer sees an available slot
+  ASSERT_TRUE(queue.BeginWrite().valid);
+  // reader caught up
+  ASSERT_TRUE(queue.BeginRead().read_pos == queue.BeginRead().write_pos);
+}
+
+}  // namespace
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/protozero/test/example_proto/test_messages.descriptor.h b/src/protozero/test/example_proto/test_messages.descriptor.h
index cdda498..417a711 100644
--- a/src/protozero/test/example_proto/test_messages.descriptor.h
+++ b/src/protozero/test/example_proto/test_messages.descriptor.h
@@ -223,19 +223,19 @@
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x42,
      0x61, 0x7a, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, 0x61,
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x6f, 0x6f, 0x4d,
-     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 0x6f,
+     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x4d, 0x6f,
      0x6f, 0x4d, 0x6f, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x52, 0x4c, 0x45,
      0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x0a, 0x75, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+     0x52, 0x0a, 0x55, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
      0x12, 0x12, 0x0a, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x04, 0x78, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
+     0x28, 0x08, 0x52, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
      0x55, 0x72, 0x4c, 0x45, 0x5f, 0x6e, 0x63, 0x6f, 0x5f, 0x5f, 0x64, 0x65,
-     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x72, 0x4c,
+     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x72, 0x4c,
      0x45, 0x4e, 0x63, 0x6f, 0x44, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x09, 0x5f,
      0x5f, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x07, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
+     0x28, 0x08, 0x52, 0x07, 0x42, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
      0x0e, 0x0a, 0x02, 0x55, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x02, 0x75, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
+     0x02, 0x55, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
      0x69, 0x67, 0x5f, 0x5f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
      0x62, 0x61, 0x6e, 0x67, 0x42, 0x69, 0x67, 0x22, 0x8f, 0x01, 0x0a, 0x14,
      0x50, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74,