Revert^2 Integrate transaction tracing with perfetto

Define the perfetto custom data source TransactionDataSource.
TransactionDataSource is registered with perfetto and is used
to listen to perfetto events (setup, start, stop, flush)
and to write trace packets to perfetto.

The user can configure/start/stop tracing via /system/bin/perfetto.

Tracing can operate in the following modes.

ACTIVE mode:
The transactions ring buffer (starting state + following committed
transactions) is written (only once) to perfetto when the 'start'
event is received.
Transactions are then written to perfetto each time they are committed.
On the receiver side, the data source is to be configured to periodically
flush data to disk providing a virtually infinite storage.

CONTINUOUS mode:
Listens to the perfetto 'flush' event (e.g. when a bugreport is taken).
When a 'flush' event is received, the ring buffer (starting state + following
committed transactions) hold by TransactionTracing is written to perfetto.
On the receiver side, the data source is to be configured with a dedicated
buffer large enough to store all the flushed data.

Bug: b/284424784
Test: atest libsurfaceflinger_unittest && atest transactiontrace_testsuite
Change-Id: I703ed53e71f442e2f6af7c4a638d2f847107167a
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index a13dc65..1718e0b 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -200,6 +200,7 @@
         "SurfaceFlingerDefaultFactory.cpp",
         "Tracing/LayerDataSource.cpp",
         "Tracing/LayerTracing.cpp",
+        "Tracing/TransactionDataSource.cpp",
         "Tracing/TransactionTracing.cpp",
         "Tracing/TransactionProtoParser.cpp",
         "Tracing/tools/LayerTraceGenerator.cpp",
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b30469d..38188c0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6941,7 +6941,7 @@
                         // Transaction tracing is always running but allow the user to temporarily
                         // increase the buffer when actively debugging.
                         mTransactionTracing->setBufferSize(
-                                TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
+                                TransactionTracing::LEGACY_ACTIVE_TRACING_BUFFER_SIZE);
                     } else {
                         TransactionTraceWriter::getInstance().invoke("", /* overwrite= */ true);
                         mTransactionTracing->setBufferSize(
@@ -6951,7 +6951,7 @@
                 reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
             }
-            case 1042: { // Write layers trace or transaction trace to file
+            case 1042: { // Write transaction trace to file
                 if (mTransactionTracing) {
                     mTransactionTracing->writeToFile();
                 }
diff --git a/services/surfaceflinger/Tracing/TransactionDataSource.cpp b/services/surfaceflinger/Tracing/TransactionDataSource.cpp
new file mode 100644
index 0000000..05b89b8
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionDataSource.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 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 "TransactionDataSource.h"
+#include "TransactionTracing.h"
+
+#undef LOG_TAG
+#define LOG_TAG "TransactionTracing"
+
+#include <log/log.h>
+
+namespace android {
+
+void TransactionDataSource::Initialize(TransactionTracing& transactionTracing) {
+    mTransactionTracing.store(&transactionTracing);
+
+    auto args = perfetto::TracingInitArgs{};
+    args.backends = perfetto::kSystemBackend;
+    perfetto::Tracing::Initialize(args);
+
+    perfetto::DataSourceDescriptor descriptor;
+    descriptor.set_name(kName);
+    TransactionDataSource::Register(descriptor);
+}
+
+void TransactionDataSource::UnregisterTransactionTracing() {
+    mTransactionTracing.store(nullptr);
+}
+
+void TransactionDataSource::OnSetup(const TransactionDataSource::SetupArgs& args) {
+    const auto configRaw = args.config->surfaceflinger_transactions_config_raw();
+    const auto config =
+            perfetto::protos::pbzero::SurfaceFlingerTransactionsConfig::Decoder{configRaw};
+
+    if (config.has_mode() && config.mode() != TransactionTracing::Mode::MODE_UNSPECIFIED) {
+        mMode = static_cast<TransactionTracing::Mode>(config.mode());
+    } else {
+        mMode = TransactionTracing::Mode::MODE_CONTINUOUS;
+        ALOGV("Received config with unspecified 'mode'. Using 'CONTINUOUS' as default");
+    }
+}
+
+void TransactionDataSource::OnStart(const StartArgs&) {
+    ALOGV("Received OnStart event");
+    if (auto* p = mTransactionTracing.load()) {
+        p->onStart(mMode);
+    }
+}
+
+void TransactionDataSource::OnFlush(const FlushArgs&) {
+    ALOGV("Received OnFlush event");
+    if (auto* p = mTransactionTracing.load()) {
+        p->onFlush(mMode);
+    }
+}
+
+void TransactionDataSource::OnStop(const StopArgs&) {
+    ALOGV("Received OnStop event");
+}
+
+TransactionTracing::Mode TransactionDataSource::GetMode() const {
+    return mMode;
+}
+
+std::atomic<TransactionTracing*> TransactionDataSource::mTransactionTracing = nullptr;
+
+} // namespace android
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::TransactionDataSource);
diff --git a/services/surfaceflinger/Tracing/TransactionDataSource.h b/services/surfaceflinger/Tracing/TransactionDataSource.h
new file mode 100644
index 0000000..be8373b
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionDataSource.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#pragma once
+
+#include "TransactionTracing.h"
+
+#include <perfetto/tracing.h>
+
+namespace android {
+
+/*
+ * Thread local storage used for fast (lock free) read of data source's properties.
+ *
+ */
+struct TransactionDataSourceTlsState {
+    template <typename TraceContext>
+    explicit TransactionDataSourceTlsState(const TraceContext& trace_context) {
+        auto dataSource = trace_context.GetDataSourceLocked();
+        mMode = dataSource.valid() ? dataSource->GetMode()
+                                   : TransactionTracing::Mode::MODE_CONTINUOUS;
+    }
+
+    TransactionTracing::Mode mMode;
+};
+
+struct TransactionDataSourceTraits : public perfetto::DefaultDataSourceTraits {
+    using TlsStateType = TransactionDataSourceTlsState;
+};
+
+/*
+ * Defines the Perfetto custom data source 'android.surfaceflinger.transactions'.
+ *
+ * Registers the data source with Perfetto, listens to Perfetto events (setup/start/flush/stop)
+ * and writes trace packets to Perfetto.
+ *
+ */
+class TransactionDataSource
+      : public perfetto::DataSource<TransactionDataSource, TransactionDataSourceTraits> {
+public:
+    static void Initialize(TransactionTracing&);
+    static void UnregisterTransactionTracing();
+    void OnSetup(const SetupArgs&) override;
+    void OnStart(const StartArgs&) override;
+    void OnFlush(const FlushArgs&) override;
+    void OnStop(const StopArgs&) override;
+    TransactionTracing::Mode GetMode() const;
+
+    static constexpr auto* kName = "android.surfaceflinger.transactions";
+    static constexpr perfetto::BufferExhaustedPolicy kBufferExhaustedPolicy =
+            perfetto::BufferExhaustedPolicy::kStall;
+    static constexpr bool kRequiresCallbacksUnderLock = false;
+
+private:
+    static std::atomic<TransactionTracing*> mTransactionTracing;
+    TransactionTracing::Mode mMode;
+};
+
+} // namespace android
+
+PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(android::TransactionDataSource);
diff --git a/services/surfaceflinger/Tracing/TransactionRingBuffer.h b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
index e6f85ca..7d1d3fd 100644
--- a/services/surfaceflinger/Tracing/TransactionRingBuffer.h
+++ b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
@@ -47,7 +47,7 @@
         mUsedInBytes = 0U;
     }
 
-    void writeToProto(FileProto& fileProto) {
+    void writeToProto(FileProto& fileProto) const {
         fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) +
                                            fileProto.entry().size());
         for (const std::string& entry : mStorage) {
@@ -56,24 +56,6 @@
         }
     }
 
-    status_t writeToFile(FileProto& fileProto, std::string filename) {
-        ATRACE_CALL();
-        writeToProto(fileProto);
-        std::string output;
-        if (!fileProto.SerializeToString(&output)) {
-            ALOGE("Could not serialize proto.");
-            return UNKNOWN_ERROR;
-        }
-
-        // -rw-r--r--
-        const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-        if (!android::base::WriteStringToFile(output, filename, mode, getuid(), getgid(), true)) {
-            ALOGE("Could not save the proto file %s", filename.c_str());
-            return PERMISSION_DENIED;
-        }
-        return NO_ERROR;
-    }
-
     status_t appendToStream(FileProto& fileProto, std::ofstream& out) {
         ATRACE_CALL();
         writeToProto(fileProto);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 8aacbca..0517984 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -25,6 +25,7 @@
 
 #include "Client.h"
 #include "FrontEnd/LayerCreationArgs.h"
+#include "TransactionDataSource.h"
 #include "TransactionTracing.h"
 
 namespace android {
@@ -34,15 +35,20 @@
       : mProtoParser(std::make_unique<TransactionProtoParser::FlingerDataMapper>()) {
     std::scoped_lock lock(mTraceLock);
 
-    mBuffer.setSize(mBufferSizeInBytes);
+    mBuffer.setSize(CONTINUOUS_TRACING_BUFFER_SIZE);
+
     mStartingTimestamp = systemTime();
+
     {
         std::scoped_lock lock(mMainThreadLock);
         mThread = std::thread(&TransactionTracing::loop, this);
     }
+
+    TransactionDataSource::Initialize(*this);
 }
 
 TransactionTracing::~TransactionTracing() {
+    TransactionDataSource::UnregisterTransactionTracing();
     std::thread thread;
     {
         std::scoped_lock lock(mMainThreadLock);
@@ -53,21 +59,94 @@
     if (thread.joinable()) {
         thread.join();
     }
+}
 
-    writeToFile();
+void TransactionTracing::onStart(TransactionTracing::Mode mode) {
+    // In "active" mode write the ring buffer (starting state + following sequence of transactions)
+    // to perfetto when tracing starts (only once).
+    if (mode != Mode::MODE_ACTIVE) {
+        return;
+    }
+
+    writeRingBufferToPerfetto(TransactionTracing::Mode::MODE_ACTIVE);
+
+    ALOGV("Started active mode tracing (wrote initial transactions ring buffer to perfetto)");
+}
+
+void TransactionTracing::onFlush(TransactionTracing::Mode mode) {
+    // In "continuous" mode write the ring buffer (starting state + following sequence of
+    // transactions) to perfetto when a "flush" event is received (bugreport is taken or tracing is
+    // stopped).
+    if (mode != Mode::MODE_CONTINUOUS) {
+        return;
+    }
+
+    writeRingBufferToPerfetto(TransactionTracing::Mode::MODE_CONTINUOUS);
+
+    ALOGV("Flushed continuous mode tracing (wrote transactions ring buffer to perfetto");
+}
+
+void TransactionTracing::writeRingBufferToPerfetto(TransactionTracing::Mode mode) {
+    // Write the ring buffer (starting state + following sequence of transactions) to perfetto
+    // tracing sessions with the specified mode.
+    const auto fileProto = writeToProto();
+
+    TransactionDataSource::Trace([&](TransactionDataSource::TraceContext context) {
+        // Write packets only to tracing sessions with specified mode
+        if (context.GetCustomTlsState()->mMode != mode) {
+            return;
+        }
+        for (const auto& entryProto : fileProto.entry()) {
+            const auto entryBytes = entryProto.SerializeAsString();
+
+            auto packet = context.NewTracePacket();
+            packet->set_timestamp(static_cast<uint64_t>(entryProto.elapsed_realtime_nanos()));
+            packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+
+            auto* transactionsProto = packet->set_surfaceflinger_transactions();
+            transactionsProto->AppendRawProtoBytes(entryBytes.data(), entryBytes.size());
+        }
+        {
+            // TODO (b/162206162): remove empty packet when perfetto bug is fixed.
+            //  It is currently needed in order not to lose the last trace entry.
+            context.NewTracePacket();
+        }
+    });
 }
 
 status_t TransactionTracing::writeToFile(const std::string& filename) {
-    std::scoped_lock lock(mTraceLock);
+    auto fileProto = writeToProto();
+
+    std::string output;
+    if (!fileProto.SerializeToString(&output)) {
+        ALOGE("Could not serialize proto.");
+        return UNKNOWN_ERROR;
+    }
+
+    // -rw-r--r--
+    const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+    if (!android::base::WriteStringToFile(output, filename, mode, getuid(), getgid(), true)) {
+        ALOGE("Could not save the proto file %s", filename.c_str());
+        return PERMISSION_DENIED;
+    }
+
+    return NO_ERROR;
+}
+
+perfetto::protos::TransactionTraceFile TransactionTracing::writeToProto() {
+    std::scoped_lock<std::mutex> lock(mTraceLock);
     perfetto::protos::TransactionTraceFile fileProto = createTraceFileProto();
-    addStartingStateToProtoLocked(fileProto);
-    return mBuffer.writeToFile(fileProto, filename);
+    const auto startingStateProto = createStartingStateProtoLocked();
+    if (startingStateProto) {
+        *fileProto.add_entry() = std::move(*startingStateProto);
+    }
+    mBuffer.writeToProto(fileProto);
+    return fileProto;
 }
 
 void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
     std::scoped_lock lock(mTraceLock);
-    mBufferSizeInBytes = bufferSizeInBytes;
-    mBuffer.setSize(mBufferSizeInBytes);
+    mBuffer.setSize(bufferSizeInBytes);
 }
 
 perfetto::protos::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
@@ -75,8 +154,8 @@
     proto.set_magic_number(
             uint64_t(perfetto::protos::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
             perfetto::protos::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
-    auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) -
-                                                   systemTime(SYSTEM_TIME_MONOTONIC));
+    auto timeOffsetNs = static_cast<uint64_t>(systemTime(SYSTEM_TIME_REALTIME) -
+                                              systemTime(SYSTEM_TIME_MONOTONIC));
     proto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs);
     proto.set_version(TRACING_VERSION);
     return proto;
@@ -206,11 +285,34 @@
 
         std::string serializedProto;
         entryProto.SerializeToString(&serializedProto);
-        entryProto.Clear();
+
+        TransactionDataSource::Trace([&](TransactionDataSource::TraceContext context) {
+            // In "active" mode write each committed transaction to perfetto.
+            // Note: the starting state is written (once) when the perfetto "start" event is
+            // received.
+            if (context.GetCustomTlsState()->mMode != Mode::MODE_ACTIVE) {
+                return;
+            }
+            {
+                auto packet = context.NewTracePacket();
+                packet->set_timestamp(static_cast<uint64_t>(entryProto.elapsed_realtime_nanos()));
+                packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+                auto* transactions = packet->set_surfaceflinger_transactions();
+                transactions->AppendRawProtoBytes(serializedProto.data(), serializedProto.size());
+            }
+            {
+                // TODO (b/162206162): remove empty packet when perfetto bug is fixed.
+                //  It is currently needed in order not to lose the last trace entry.
+                context.NewTracePacket();
+            }
+        });
+
         std::vector<std::string> entries = mBuffer.emplace(std::move(serializedProto));
         removedEntries.reserve(removedEntries.size() + entries.size());
         removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
                               std::make_move_iterator(entries.end()));
+
+        entryProto.Clear();
     }
 
     perfetto::protos::TransactionTraceEntry removedEntryProto;
@@ -309,44 +411,38 @@
     }
 }
 
-void TransactionTracing::addStartingStateToProtoLocked(
-        perfetto::protos::TransactionTraceFile& proto) {
-    if (mStartingStates.size() == 0) {
-        return;
+std::optional<perfetto::protos::TransactionTraceEntry>
+TransactionTracing::createStartingStateProtoLocked() {
+    if (mStartingStates.empty()) {
+        return std::nullopt;
     }
 
-    perfetto::protos::TransactionTraceEntry* entryProto = proto.add_entry();
-    entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
-    entryProto->set_vsync_id(0);
+    perfetto::protos::TransactionTraceEntry entryProto;
+    entryProto.set_elapsed_realtime_nanos(mStartingTimestamp);
+    entryProto.set_vsync_id(0);
 
-    entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
+    entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
     for (auto& [layerId, state] : mStartingStates) {
-        entryProto->mutable_added_layers()->Add(mProtoParser.toProto(state.args));
+        entryProto.mutable_added_layers()->Add(mProtoParser.toProto(state.args));
     }
 
     perfetto::protos::TransactionState transactionProto = mProtoParser.toProto(mStartingStates);
     transactionProto.set_vsync_id(0);
     transactionProto.set_post_time(mStartingTimestamp);
-    entryProto->mutable_transactions()->Add(std::move(transactionProto));
+    entryProto.mutable_transactions()->Add(std::move(transactionProto));
 
-    entryProto->mutable_destroyed_layer_handles()->Reserve(
+    entryProto.mutable_destroyed_layer_handles()->Reserve(
             static_cast<int32_t>(mRemovedLayerHandlesAtStart.size()));
     for (const uint32_t destroyedLayerHandleId : mRemovedLayerHandlesAtStart) {
-        entryProto->mutable_destroyed_layer_handles()->Add(destroyedLayerHandleId);
+        entryProto.mutable_destroyed_layer_handles()->Add(destroyedLayerHandleId);
     }
 
-    entryProto->mutable_displays()->Reserve(static_cast<int32_t>(mStartingDisplayInfos.size()));
+    entryProto.mutable_displays()->Reserve(static_cast<int32_t>(mStartingDisplayInfos.size()));
     for (auto& [layerStack, displayInfo] : mStartingDisplayInfos) {
-        entryProto->mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
+        entryProto.mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
     }
-}
 
-perfetto::protos::TransactionTraceFile TransactionTracing::writeToProto() {
-    std::scoped_lock<std::mutex> lock(mTraceLock);
-    perfetto::protos::TransactionTraceFile proto = createTraceFileProto();
-    addStartingStateToProtoLocked(proto);
-    mBuffer.writeToProto(proto);
-    return proto;
+    return entryProto;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 09fcd8a..ddbf3e4 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -22,8 +22,8 @@
 #include <utils/Singleton.h>
 #include <utils/Timers.h>
 
-#include <memory>
 #include <mutex>
+#include <optional>
 #include <thread>
 
 #include "FrontEnd/DisplayInfo.h"
@@ -41,7 +41,7 @@
 class TransactionTracingTest;
 
 /*
- * Records all committed transactions into a ring bufffer.
+ * Records all committed transactions into a ring buffer.
  *
  * Transactions come in via the binder thread. They are serialized to proto
  * and stored in a map using the transaction id as key. Main thread will
@@ -49,26 +49,104 @@
  * the tracing thread. The tracing thread will then wake up and add the
  * committed transactions to the ring buffer.
  *
- * When generating SF dump state, we will flush the buffer to a file which
- * will then be included in the bugreport.
+ * The traced data can then be collected via:
+ * - Perfetto (preferred).
+ * - File system, after triggering the disk write through SF backdoor. This is legacy and is going
+ *   to be phased out.
+ *
+ * The Perfetto custom data source TransactionDataSource is registered with perfetto and is used
+ * to listen to perfetto events (setup, start, stop, flush) and to write trace packets to perfetto.
+ *
+ * The user can configure/start/stop tracing via /system/bin/perfetto.
+ *
+ * Tracing can operate in the following modes.
+ *
+ * ACTIVE mode:
+ * The transactions ring buffer (starting state + following committed transactions) is written
+ * (only once) to perfetto when the 'start' event is received.
+ * Transactions are then written to perfetto each time they are committed.
+ * On the receiver side, the data source is to be configured to periodically
+ * flush data to disk providing virtually infinite storage.
+ *
+ * CONTINUOUS mode:
+ * Listens to the perfetto 'flush' event (e.g. when a bugreport is taken).
+ * When a 'flush' event is received, the ring buffer of transactions (starting state + following
+ * committed transactions) is written to perfetto. On the receiver side, the data source is to be
+ * configured with a dedicated buffer large enough to store all the flushed data.
+ *
+ *
+ * E.g. start active mode tracing:
+ *
+   adb shell perfetto \
+     -c - --txt \
+     -o /data/misc/perfetto-traces/trace \
+   <<EOF
+   unique_session_name: "surfaceflinger_transactions_active"
+   buffers: {
+       size_kb: 1024
+       fill_policy: RING_BUFFER
+   }
+   data_sources: {
+       config {
+           name: "android.surfaceflinger.transactions"
+           surfaceflinger_transactions_config: {
+               mode: MODE_ACTIVE
+           }
+       }
+   }
+   write_into_file: true
+   file_write_period_ms: 100
+   EOF
+ *
+ *
+ * E.g. start continuous mode tracing:
+ *
+   adb shell perfetto \
+     -c - --txt \
+     -o /data/misc/perfetto-traces/trace \
+   <<EOF
+   unique_session_name: "surfaceflinger_transactions_continuous"
+   buffers: {
+       size_kb: 1024
+       fill_policy: RING_BUFFER
+   }
+   data_sources: {
+       config {
+           name: "android.surfaceflinger.transactions"
+           surfaceflinger_transactions_config: {
+               mode: MODE_CONTINUOUS
+           }
+       }
+   }
+   EOF
  *
  */
 class TransactionTracing {
 public:
+    using Mode = perfetto::protos::pbzero::SurfaceFlingerTransactionsConfig::Mode;
+
     TransactionTracing();
     ~TransactionTracing();
 
+    // Start event from perfetto data source
+    void onStart(Mode mode);
+    // Flush event from perfetto data source
+    void onFlush(Mode mode);
+
     void addQueuedTransaction(const TransactionState&);
     void addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
                                   const frontend::DisplayInfos&, bool displayInfoChanged);
     status_t writeToFile(const std::string& filename = FILE_PATH);
+    // Return buffer contents as trace file proto
+    perfetto::protos::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
     void setBufferSize(size_t bufferSizeInBytes);
     void onLayerRemoved(int layerId);
     void dump(std::string&) const;
     // Wait until all the committed transactions for the specified vsync id are added to the buffer.
     void flush() EXCLUDES(mMainThreadLock);
+
     static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
-    static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
+    static constexpr auto LEGACY_ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
     // version 1 - switching to support new frontend
     static constexpr auto TRACING_VERSION = 1;
 
@@ -88,7 +166,6 @@
     TransactionRingBuffer<perfetto::protos::TransactionTraceFile,
                           perfetto::protos::TransactionTraceEntry>
             mBuffer GUARDED_BY(mTraceLock);
-    size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
     std::unordered_map<uint64_t, perfetto::protos::TransactionState> mQueuedTransactions
             GUARDED_BY(mTraceLock);
     LocklessStack<perfetto::protos::TransactionState> mTransactionQueue;
@@ -124,19 +201,17 @@
     std::vector<uint32_t /* layerId */> mPendingDestroyedLayers; // only accessed by main thread
     int64_t mLastUpdatedVsyncId = -1;
 
+    void writeRingBufferToPerfetto(TransactionTracing::Mode mode);
     perfetto::protos::TransactionTraceFile createTraceFileProto() const;
     void loop();
     void addEntry(const std::vector<CommittedUpdates>& committedTransactions,
                   const std::vector<uint32_t>& removedLayers) EXCLUDES(mTraceLock);
     int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
     void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
-    void addStartingStateToProtoLocked(perfetto::protos::TransactionTraceFile& proto)
+    std::optional<perfetto::protos::TransactionTraceEntry> createStartingStateProtoLocked()
             REQUIRES(mTraceLock);
     void updateStartingStateLocked(const perfetto::protos::TransactionTraceEntry& entry)
             REQUIRES(mTraceLock);
-    // TEST
-    // Return buffer contents as trace file proto
-    perfetto::protos::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
 };
 
 class TransactionTraceWriter : public Singleton<TransactionTraceWriter> {