Merge "SF: Track starting layer state with transaction tracing"
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a648797..21f3872 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4407,6 +4407,16 @@
return result;
}
+ int parentId = -1;
+ // We can safely promote the layer in binder thread because we have a strong reference
+ // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
+ sp<Layer> parentSp = parent.promote();
+ if (parentSp != nullptr) {
+ parentId = parentSp->getSequence();
+ }
+ mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+ args.flags, parentId);
+
setTransactionFlags(eTransactionNeeded);
*outLayerId = layer->sequence;
return result;
@@ -6642,6 +6652,7 @@
if (!layer->isRemovedFromCurrentState()) {
mScheduler->deregisterLayer(layer);
}
+ mTransactionTracing.onLayerRemoved(layer->getSequence());
}
void SurfaceFlinger::onLayerUpdate() {
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 84890ee..d136e0b 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -53,6 +53,7 @@
mEnabled = false;
LayersTraceFileProto fileProto = createTraceFileProto();
mBuffer->writeToFile(fileProto, FILE_NAME);
+ mBuffer->reset();
return true;
}
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
index 63a2786..281cd19 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -39,26 +39,28 @@
void setSize(size_t newSize) { mSizeInBytes = newSize; }
EntryProto& front() { return mStorage.front(); }
const EntryProto& front() const { return mStorage.front(); }
+ const EntryProto& back() const { return mStorage.back(); }
- void reset(size_t newSize) {
+ void reset() {
// use the swap trick to make sure memory is released
- std::queue<EntryProto>().swap(mStorage);
- mSizeInBytes = newSize;
+ std::deque<EntryProto>().swap(mStorage);
mUsedInBytes = 0U;
}
- void flush(FileProto& fileProto) {
- fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
- while (!mStorage.empty()) {
- auto entry = fileProto.add_entry();
- entry->Swap(&mStorage.front());
- mStorage.pop();
+
+ void writeToProto(FileProto& fileProto) {
+ fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) +
+ fileProto.entry().size());
+ for (const EntryProto& entry : mStorage) {
+ EntryProto* entryProto = fileProto.add_entry();
+ *entryProto = entry;
}
}
status_t writeToFile(FileProto& fileProto, std::string filename) {
ATRACE_CALL();
+ writeToProto(fileProto);
std::string output;
- if (!writeToString(fileProto, &output)) {
+ if (!fileProto.SerializeToString(&output)) {
ALOGE("Could not serialize proto.");
return UNKNOWN_ERROR;
}
@@ -72,13 +74,6 @@
return NO_ERROR;
}
- bool writeToString(FileProto& fileProto, std::string* outString) {
- ATRACE_CALL();
- flush(fileProto);
- reset(mSizeInBytes);
- return fileProto.SerializeToString(outString);
- }
-
std::vector<EntryProto> emplace(EntryProto&& proto) {
std::vector<EntryProto> replacedEntries;
size_t protoSize = static_cast<size_t>(proto.ByteSize());
@@ -88,10 +83,10 @@
}
mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
replacedEntries.emplace_back(mStorage.front());
- mStorage.pop();
+ mStorage.pop_front();
}
mUsedInBytes += protoSize;
- mStorage.emplace();
+ mStorage.emplace_back();
mStorage.back().Swap(&proto);
return replacedEntries;
}
@@ -112,7 +107,7 @@
private:
size_t mUsedInBytes = 0U;
size_t mSizeInBytes = 0U;
- std::queue<EntryProto> mStorage;
+ std::deque<EntryProto> mStorage;
};
} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index d1dc076..7e12313 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -43,7 +43,7 @@
}
proto::TransactionState TransactionProtoParser::toProto(
- std::vector<std::pair<int32_t /* layerId */, TracingLayerState>> states) {
+ const std::unordered_map<int32_t /* layerId */, TracingLayerState> states) {
proto::TransactionState proto;
for (auto& [layerId, state] : states) {
proto::LayerState layerProto = toProto(state, nullptr);
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index e8a139f..619ee05 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -51,7 +51,7 @@
static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn,
DisplayHandleToIdFn getDisplayIdFn);
static proto::TransactionState toProto(
- std::vector<std::pair<int32_t /* layerId */, TracingLayerState>>);
+ const std::unordered_map<int32_t /* layerId */, TracingLayerState>);
static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
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
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index d92ab01..0aa22ed 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -62,6 +62,9 @@
void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
status_t writeToFile();
void setBufferSize(size_t bufferSizeInBytes);
+ void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
+ int parentId);
+ void onLayerRemoved(int layerId);
void dump(std::string&) const;
static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
@@ -78,6 +81,12 @@
size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
GUARDED_BY(mTraceLock);
+ nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
+ std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
+ std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+ GUARDED_BY(mTraceLock);
+ std::unordered_map<int32_t /* layerId */, TracingLayerState> mStartingStates
+ GUARDED_BY(mTraceLock);
// We do not want main thread to block so main thread will try to acquire mMainThreadLock,
// otherwise will push data to temporary container.
@@ -93,15 +102,25 @@
};
std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
- proto::TransactionTraceFile createTraceFileProto() const;
+ std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
+ std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+
+ proto::TransactionTraceFile createTraceFileProto() const;
void loop();
- void addEntry(const std::vector<CommittedTransactions>& committedTransactions)
- EXCLUDES(mTraceLock);
+ void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+ const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+ int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
+ void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
+ void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
+ void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
+ status_t writeToFileLocked() REQUIRES(mTraceLock);
// TEST
- // Wait until all the committed transactions are added to the buffer.
- void flush() EXCLUDES(mMainThreadLock);
+ // Wait until all the committed transactions for the specified vsync id are added to the buffer.
+ void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
+ // Return buffer contents as trace file proto
+ proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
};
} // namespace android
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index edeacfa..10222cc 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -42,8 +42,10 @@
int64 elapsed_realtime_nanos = 1;
int64 vsync_id = 2;
repeated TransactionState transactions = 3;
- repeated LayerCreationArgs new_layers = 4;
- repeated DisplayState new_displays = 5;
+ repeated LayerCreationArgs added_layers = 4;
+ repeated int32 removed_layers = 5;
+ repeated DisplayState added_displays = 6;
+ repeated int32 removed_displays = 7;
}
message LayerCreationArgs {
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 2afa68a..ffe5671 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -28,11 +28,14 @@
class TransactionTracingTest : public testing::Test {
protected:
+ static constexpr size_t SMALL_BUFFER_SIZE = 1024;
std::unique_ptr<android::TransactionTracing> mTracing;
-
void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); }
- void TearDown() override { mTracing.reset(); }
+ void TearDown() override {
+ mTracing->disable();
+ mTracing.reset();
+ }
auto getCommittedTransactions() {
std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock);
@@ -49,7 +52,7 @@
return mTracing->mBuffer->used();
}
- auto flush() { return mTracing->flush(); }
+ auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); }
auto bufferFront() {
std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
@@ -61,12 +64,28 @@
return mTracing->mThread.joinable();
}
- std::string writeToString() {
+ proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); }
+
+ auto getCreatedLayers() {
std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
- std::string output;
- proto::TransactionTraceFile fileProto = mTracing->createTraceFileProto();
- mTracing->mBuffer->writeToString(fileProto, &output);
- return output;
+ return mTracing->mCreatedLayers;
+ }
+
+ auto getStartingStates() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mStartingStates;
+ }
+
+ void queueAndCommitTransaction(int64_t vsyncId) {
+ TransactionState transaction;
+ transaction.id = static_cast<uint64_t>(vsyncId * 3);
+ transaction.originUid = 1;
+ transaction.originPid = 2;
+ mTracing->addQueuedTransaction(transaction);
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ mTracing->addCommittedTransactions(transactions, vsyncId);
+ flush(vsyncId);
}
// Test that we clean up the tracing thread and free any memory allocated.
@@ -76,6 +95,7 @@
EXPECT_EQ(getCommittedTransactions().size(), 0u);
EXPECT_EQ(getQueuedTransactions().size(), 0u);
EXPECT_EQ(getUsedBufferSize(), 0u);
+ EXPECT_EQ(getStartingStates().size(), 0u);
}
void verifyEntry(const proto::TransactionTraceEntry& actualProto,
@@ -122,17 +142,150 @@
std::vector<TransactionState> secondTransactionSet =
std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
- flush();
+ flush(secondTransactionSetVsyncId);
- std::string protoString = writeToString();
- proto::TransactionTraceFile proto;
- proto.ParseFromString(protoString);
- EXPECT_EQ(proto.entry().size(), 2);
- verifyEntry(proto.entry(0), firstTransactionSet, firstTransactionSetVsyncId);
- verifyEntry(proto.entry(1), secondTransactionSet, secondTransactionSetVsyncId);
+ proto::TransactionTraceFile proto = writeToProto();
+ EXPECT_EQ(proto.entry().size(), 3);
+ // skip starting entry
+ verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId);
+ verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId);
mTracing->disable();
verifyDisabledTracingState();
}
+class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
+protected:
+ void SetUp() override {
+ TransactionTracingTest::SetUp();
+ mTracing->enable();
+ // add layers
+ mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+ const sp<IBinder> fakeLayerHandle = new BBinder();
+ mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
+ 123 /* flags */, -1 /* parentId */);
+ const sp<IBinder> fakeChildLayerHandle = new BBinder();
+ mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), 2 /* layerId */, "child",
+ 456 /* flags */, mParentLayerId);
+
+ // add some layer transaction
+ {
+ TransactionState transaction;
+ transaction.id = 50;
+ ComposerState layerState;
+ layerState.state.surface = fakeLayerHandle;
+ layerState.state.what = layer_state_t::eLayerChanged;
+ layerState.state.z = 42;
+ transaction.states.add(layerState);
+ ComposerState childState;
+ childState.state.surface = fakeChildLayerHandle;
+ layerState.state.z = 43;
+ transaction.states.add(childState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
+ mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+ flush(VSYNC_ID_FIRST_LAYER_CHANGE);
+ }
+
+ // add transactions that modify the layer state further so we can test that layer state
+ // gets merged
+ {
+ TransactionState transaction;
+ transaction.id = 51;
+ ComposerState layerState;
+ layerState.state.surface = fakeLayerHandle;
+ layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
+ layerState.state.z = 41;
+ layerState.state.x = 22;
+ transaction.states.add(layerState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
+ mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+ flush(VSYNC_ID_SECOND_LAYER_CHANGE);
+ }
+
+ // remove child layer
+ mTracing->onLayerRemoved(2);
+ VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId;
+ queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED);
+
+ // remove layer
+ mTracing->onLayerRemoved(1);
+ queueAndCommitTransaction(++mVsyncId);
+ }
+
+ void TearDown() override {
+ mTracing->disable();
+ verifyDisabledTracingState();
+ TransactionTracingTest::TearDown();
+ }
+
+ int mParentLayerId = 1;
+ int64_t mVsyncId = 0;
+ int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
+ int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
+ int64_t VSYNC_ID_CHILD_LAYER_REMOVED;
+};
+
+TEST_F(TransactionTracingLayerHandlingTest, addStartingState) {
+ // add transactions until we drop the transaction with the first layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_FIRST_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify we can still retrieve the layer change from the first entry containing starting
+ // states.
+ EXPECT_GT(proto.entry().size(), 0);
+ EXPECT_GT(proto.entry(0).transactions().size(), 0);
+ EXPECT_GT(proto.entry(0).added_layers().size(), 0);
+ EXPECT_GT(proto.entry(0).transactions(0).layer_changes().size(), 0);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) {
+ // add transactions until we drop the transaction with the second layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify starting states are updated correctly
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) {
+ // add transactions until we drop the transaction which removes the child layer
+ while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify the child layer has been removed from the trace
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) {
+ // add transactions until we drop the transaction with the second layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify we have two starting states
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+
+ // Continue adding transactions until child layer is removed
+ while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto = writeToProto();
+ // verify we still have the parent layer state
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
} // namespace android