SF: Track starting layer state with transaction tracing

In order to recreate the layer state from transaction traces,
we need to know the starting layer state. We need to start
with an initial state, then replay the transactions from the
trace to recreate the layer states.

Keeping track of the initial layer state while maintaining
a ring buffer of transactions is expensive since it would
require accessing the drawing state from the tracing
thread.

This cl builds and updates a transaction that will
recreate the layer's starting state. As transactions are
evicted from the ring buffer, they are used to update
the starting state transactions.

Test: presubmit
Bug: 200284593
Change-Id: Ifaba8fb061fca4acc15df661483217552011aa09
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 758bd31..cf488c2 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -41,6 +41,7 @@
         return false;
     }
     mBuffer->setSize(mBufferSizeInBytes);
+    mStartingTimestamp = systemTime();
     mEnabled = true;
     {
         std::scoped_lock lock(mMainThreadLock);
@@ -68,9 +69,11 @@
     }
     mEnabled = false;
 
-    proto::TransactionTraceFile fileProto = createTraceFileProto();
-    mBuffer->writeToFile(fileProto, FILE_NAME);
+    writeToFileLocked();
+    mBuffer->reset();
     mQueuedTransactions.clear();
+    mStartingStates.clear();
+    mLayerHandles.clear();
     return true;
 }
 
@@ -84,7 +87,12 @@
     if (!mEnabled) {
         return STATUS_OK;
     }
+    return writeToFileLocked();
+}
+
+status_t TransactionTracing::writeToFileLocked() {
     proto::TransactionTraceFile fileProto = createTraceFileProto();
+    addStartingStateToProtoLocked(fileProto);
     return mBuffer->writeToFile(fileProto, FILE_NAME);
 }
 
@@ -105,8 +113,10 @@
     std::scoped_lock lock(mTraceLock);
     base::StringAppendF(&result, "Transaction tracing state: %s\n",
                         mEnabled ? "enabled" : "disabled");
-    base::StringAppendF(&result, "  queued transactions: %d\n",
-                        static_cast<uint32_t>(mQueuedTransactions.size()));
+    base::StringAppendF(&result,
+                        "  queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
+                        mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
+                        mStartingStates.size());
     mBuffer->dump(result);
 }
 
@@ -117,7 +127,10 @@
         return;
     }
     mQueuedTransactions[transaction.id] =
-            TransactionProtoParser::toProto(transaction, nullptr, nullptr);
+            TransactionProtoParser::toProto(transaction,
+                                            std::bind(&TransactionTracing::getLayerIdLocked, this,
+                                                      std::placeholders::_1),
+                                            nullptr);
 }
 
 void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
@@ -130,25 +143,14 @@
         committedTransactions.transactionIds.emplace_back(transaction.id);
     }
 
-    // Try to acquire the lock from main thread, but don't block if we cannot acquire the lock. Add
-    // it to pending transactions that we can collect later.
-    if (mMainThreadLock.try_lock()) {
-        // We got the lock! Collect any pending transactions and continue.
-        mCommittedTransactions.insert(mCommittedTransactions.end(),
-                                      std::make_move_iterator(mPendingTransactions.begin()),
-                                      std::make_move_iterator(mPendingTransactions.end()));
-        mPendingTransactions.clear();
-        mCommittedTransactions.emplace_back(committedTransactions);
-        mTransactionsAvailableCv.notify_one();
-        mMainThreadLock.unlock();
-    } else {
-        mPendingTransactions.emplace_back(committedTransactions);
-    }
+    mPendingTransactions.emplace_back(committedTransactions);
+    tryPushToTracingThread();
 }
 
 void TransactionTracing::loop() {
     while (true) {
         std::vector<CommittedTransactions> committedTransactions;
+        std::vector<int32_t> removedLayers;
         {
             std::unique_lock<std::mutex> lock(mMainThreadLock);
             base::ScopedLockAssertion assumeLocked(mMainThreadLock);
@@ -157,19 +159,22 @@
             });
             if (mDone) {
                 mCommittedTransactions.clear();
+                mRemovedLayers.clear();
                 break;
             }
+
+            removedLayers = std::move(mRemovedLayers);
+            mRemovedLayers.clear();
             committedTransactions = std::move(mCommittedTransactions);
             mCommittedTransactions.clear();
         } // unlock mMainThreadLock
 
-        addEntry(committedTransactions);
-
-        mTransactionsAddedToBufferCv.notify_one();
+        addEntry(committedTransactions, removedLayers);
     }
 }
 
-void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions) {
+void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+                                  const std::vector<int32_t>& removedLayers) {
     ATRACE_CALL();
     std::scoped_lock lock(mTraceLock);
     std::vector<proto::TransactionTraceEntry> removedEntries;
@@ -177,6 +182,15 @@
         proto::TransactionTraceEntry entryProto;
         entryProto.set_elapsed_realtime_nanos(entry.timestamp);
         entryProto.set_vsync_id(entry.vsyncId);
+        entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size()));
+        for (auto& newLayer : mCreatedLayers) {
+            entryProto.mutable_added_layers()->Add(std::move(newLayer));
+        }
+        entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
+        for (auto& removedLayer : removedLayers) {
+            entryProto.mutable_removed_layers()->Add(removedLayer);
+        }
+        mCreatedLayers.clear();
         entryProto.mutable_transactions()->Reserve(
                 static_cast<int32_t>(entry.transactionIds.size()));
         for (const uint64_t& id : entry.transactionIds) {
@@ -188,16 +202,128 @@
                 ALOGE("Could not find transaction id %" PRIu64, id);
             }
         }
-        mBuffer->emplace(std::move(entryProto));
+        std::vector<proto::TransactionTraceEntry> entries = mBuffer->emplace(std::move(entryProto));
+        removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
+                              std::make_move_iterator(entries.end()));
+    }
+
+    for (const proto::TransactionTraceEntry& removedEntry : removedEntries) {
+        updateStartingStateLocked(removedEntry);
+    }
+    mTransactionsAddedToBufferCv.notify_one();
+}
+
+void TransactionTracing::flush(int64_t vsyncId) {
+    while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+        tryPushToTracingThread();
+    }
+    std::unique_lock<std::mutex> lock(mTraceLock);
+    base::ScopedLockAssertion assumeLocked(mTraceLock);
+    mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
+        return mBuffer->used() > 0 && mBuffer->back().vsync_id() >= vsyncId;
+    });
+}
+
+void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+                                      uint32_t flags, int parentId) {
+    std::scoped_lock lock(mTraceLock);
+    TracingLayerCreationArgs args{layerId, name, flags, parentId};
+    mLayerHandles[layerHandle] = layerId;
+    mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
+}
+
+void TransactionTracing::onLayerRemoved(int32_t layerId) {
+    mPendingRemovedLayers.emplace_back(layerId);
+    tryPushToTracingThread();
+}
+
+void TransactionTracing::tryPushToTracingThread() {
+    // Try to acquire the lock from main thread.
+    if (mMainThreadLock.try_lock()) {
+        // We got the lock! Collect any pending transactions and continue.
+        mCommittedTransactions.insert(mCommittedTransactions.end(),
+                                      std::make_move_iterator(mPendingTransactions.begin()),
+                                      std::make_move_iterator(mPendingTransactions.end()));
+        mPendingTransactions.clear();
+        mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
+                              mPendingRemovedLayers.end());
+        mPendingRemovedLayers.clear();
+        mTransactionsAvailableCv.notify_one();
+        mMainThreadLock.unlock();
+    } else {
+        ALOGV("Couldn't get lock");
     }
 }
 
-void TransactionTracing::flush() {
-    std::unique_lock<std::mutex> lock(mMainThreadLock);
-    base::ScopedLockAssertion assumeLocked(mMainThreadLock);
-    mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
-        return mCommittedTransactions.empty();
-    });
+int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) {
+    if (layerHandle == nullptr) {
+        return -1;
+    }
+    auto it = mLayerHandles.find(layerHandle->localBinder());
+    return it == mLayerHandles.end() ? -1 : it->second;
+}
+
+void TransactionTracing::updateStartingStateLocked(
+        const proto::TransactionTraceEntry& removedEntry) {
+    // Keep track of layer starting state so we can reconstruct the layer state as we purge
+    // transactions from the buffer.
+    for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
+        TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
+        startingState.layerId = addedLayer.layer_id();
+        startingState.name = addedLayer.name();
+        startingState.layerCreationFlags = addedLayer.flags();
+        startingState.parentId = addedLayer.parent_id();
+    }
+
+    // Merge layer states to starting transaction state.
+    for (const proto::TransactionState& transaction : removedEntry.transactions()) {
+        for (const proto::LayerState& layerState : transaction.layer_changes()) {
+            auto it = mStartingStates.find(layerState.layer_id());
+            if (it == mStartingStates.end()) {
+                ALOGE("Could not find layer id %d", layerState.layer_id());
+                continue;
+            }
+            TransactionProtoParser::fromProto(layerState, nullptr, it->second);
+        }
+    }
+
+    // Clean up stale starting states since the layer has been removed and the buffer does not
+    // contain any references to the layer.
+    for (const int32_t removedLayerId : removedEntry.removed_layers()) {
+        auto it = std::find_if(mLayerHandles.begin(), mLayerHandles.end(),
+                               [removedLayerId](auto& layer) {
+                                   return layer.second == removedLayerId;
+                               });
+        if (it != mLayerHandles.end()) {
+            mLayerHandles.erase(it);
+        }
+        mStartingStates.erase(removedLayerId);
+    }
+}
+
+void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
+    proto::TransactionTraceEntry* entryProto = proto.add_entry();
+    entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
+    entryProto->set_vsync_id(0);
+    entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
+    for (auto& [layerId, state] : mStartingStates) {
+        TracingLayerCreationArgs args{layerId, state.name, state.layerCreationFlags,
+                                      state.parentId};
+        entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(args));
+    }
+
+    proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates);
+    transactionProto.set_vsync_id(0);
+    transactionProto.set_post_time(mStartingTimestamp);
+    entryProto->mutable_transactions()->Add(std::move(transactionProto));
+}
+
+proto::TransactionTraceFile TransactionTracing::writeToProto() {
+    std::scoped_lock<std::mutex> lock(mTraceLock);
+    proto::TransactionTraceFile proto = createTraceFileProto();
+    addStartingStateToProtoLocked(proto);
+    mBuffer->writeToProto(proto);
+    return proto;
 }
 
 } // namespace android