blob: d5e837f8792cb55f316456851745d6042c7bf509 [file] [log] [blame]
/*
* Copyright 2021 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.
*/
#undef LOG_TAG
#define LOG_TAG "TransactionTracing"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/stringprintf.h>
#include <log/log.h>
#include <utils/SystemClock.h>
#include <utils/Trace.h>
#include "ClientCache.h"
#include "TransactionTracing.h"
#include "renderengine/ExternalTexture.h"
namespace android {
class FlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
public:
FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
: mLayerHandles(layerHandles) {}
int32_t getLayerId(const sp<IBinder>& layerHandle) const override {
if (layerHandle == nullptr) {
return -1;
}
auto it = mLayerHandles.find(layerHandle->localBinder());
if (it == mLayerHandles.end()) {
ALOGW("Could not find layer handle %p", layerHandle->localBinder());
return -1;
}
return it->second;
}
void getGraphicBufferPropertiesFromCache(client_cache_t cachedBuffer, uint64_t* outBufferId,
uint32_t* outWidth, uint32_t* outHeight,
int32_t* outPixelFormat,
uint64_t* outUsage) const override {
std::shared_ptr<renderengine::ExternalTexture> buffer =
ClientCache::getInstance().get(cachedBuffer);
if (!buffer || !buffer->getBuffer()) {
*outBufferId = 0;
*outWidth = 0;
*outHeight = 0;
*outPixelFormat = 0;
*outUsage = 0;
return;
}
*outBufferId = buffer->getId();
*outWidth = buffer->getWidth();
*outHeight = buffer->getHeight();
*outPixelFormat = buffer->getPixelFormat();
*outUsage = buffer->getUsage();
return;
}
};
TransactionTracing::TransactionTracing()
: mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)) {
std::scoped_lock lock(mTraceLock);
mBuffer.setSize(mBufferSizeInBytes);
mStartingTimestamp = systemTime();
{
std::scoped_lock lock(mMainThreadLock);
mThread = std::thread(&TransactionTracing::loop, this);
}
}
TransactionTracing::~TransactionTracing() {
std::thread thread;
{
std::scoped_lock lock(mMainThreadLock);
mDone = true;
mTransactionsAvailableCv.notify_all();
thread = std::move(mThread);
}
if (thread.joinable()) {
thread.join();
}
writeToFile();
}
status_t TransactionTracing::writeToFile(std::string filename) {
std::scoped_lock lock(mTraceLock);
proto::TransactionTraceFile fileProto = createTraceFileProto();
addStartingStateToProtoLocked(fileProto);
return mBuffer.writeToFile(fileProto, filename);
}
void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
std::scoped_lock lock(mTraceLock);
mBufferSizeInBytes = bufferSizeInBytes;
mBuffer.setSize(mBufferSizeInBytes);
}
proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
proto::TransactionTraceFile proto;
proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
return proto;
}
void TransactionTracing::dump(std::string& result) const {
std::scoped_lock lock(mTraceLock);
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);
}
void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
std::scoped_lock lock(mTraceLock);
ATRACE_CALL();
mQueuedTransactions[transaction.id] = mProtoParser.toProto(transaction);
}
void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
int64_t vsyncId) {
CommittedTransactions committedTransactions;
committedTransactions.vsyncId = vsyncId;
committedTransactions.timestamp = systemTime();
committedTransactions.transactionIds.reserve(transactions.size());
for (const auto& transaction : transactions) {
committedTransactions.transactionIds.emplace_back(transaction.id);
}
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);
mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
return mDone || !mCommittedTransactions.empty();
});
if (mDone) {
mCommittedTransactions.clear();
mRemovedLayers.clear();
break;
}
removedLayers = std::move(mRemovedLayers);
mRemovedLayers.clear();
committedTransactions = std::move(mCommittedTransactions);
mCommittedTransactions.clear();
} // unlock mMainThreadLock
if (!committedTransactions.empty() || !removedLayers.empty()) {
addEntry(committedTransactions, removedLayers);
}
}
}
void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
const std::vector<int32_t>& removedLayers) {
ATRACE_CALL();
std::scoped_lock lock(mTraceLock);
std::vector<std::string> removedEntries;
proto::TransactionTraceEntry entryProto;
for (const CommittedTransactions& entry : committedTransactions) {
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) {
auto it = mQueuedTransactions.find(id);
if (it != mQueuedTransactions.end()) {
entryProto.mutable_transactions()->Add(std::move(it->second));
mQueuedTransactions.erase(it);
} else {
ALOGW("Could not find transaction id %" PRIu64, id);
}
}
std::string serializedProto;
entryProto.SerializeToString(&serializedProto);
entryProto.Clear();
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.mutable_removed_layer_handles()->Reserve(
static_cast<int32_t>(mRemovedLayerHandles.size()));
for (auto& handle : mRemovedLayerHandles) {
entryProto.mutable_removed_layer_handles()->Add(handle);
}
mRemovedLayerHandles.clear();
}
proto::TransactionTraceEntry removedEntryProto;
for (const std::string& removedEntry : removedEntries) {
removedEntryProto.ParseFromString(removedEntry);
updateStartingStateLocked(removedEntryProto);
removedEntryProto.Clear();
}
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) {
proto::TransactionTraceEntry entry;
if (mBuffer.used() > 0) {
entry.ParseFromString(mBuffer.back());
}
return mBuffer.used() > 0 && entry.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, -1 /* mirrorFromId */};
if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
ALOGW("Duplicate handles found. %p", layerHandle);
}
mLayerHandles[layerHandle] = layerId;
mCreatedLayers.push_back(mProtoParser.toProto(args));
}
void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
const std::string& name, int mirrorFromId) {
std::scoped_lock lock(mTraceLock);
TracingLayerCreationArgs args{layerId, name, 0 /* flags */, -1 /* parentId */, mirrorFromId};
if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
ALOGW("Duplicate handles found. %p", layerHandle);
}
mLayerHandles[layerHandle] = layerId;
mCreatedLayers.emplace_back(mProtoParser.toProto(args));
}
void TransactionTracing::onLayerRemoved(int32_t layerId) {
mPendingRemovedLayers.emplace_back(layerId);
tryPushToTracingThread();
}
void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
std::scoped_lock lock(mTraceLock);
auto it = mLayerHandles.find(layerHandle);
if (it == mLayerHandles.end()) {
ALOGW("handle not found. %p", layerHandle);
return;
}
mRemovedLayerHandles.push_back(it->second);
mLayerHandles.erase(it);
}
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::updateStartingStateLocked(
const proto::TransactionTraceEntry& removedEntry) {
mStartingTimestamp = removedEntry.elapsed_realtime_nanos();
// 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();
mProtoParser.fromProto(addedLayer, startingState.args);
}
// 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()) {
ALOGW("Could not find layer id %d", layerState.layer_id());
continue;
}
mProtoParser.mergeFromProto(layerState, 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()) {
mStartingStates.erase(removedLayerId);
}
}
void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
if (mStartingStates.size() == 0) {
return;
}
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) {
entryProto->mutable_added_layers()->Add(mProtoParser.toProto(state.args));
}
proto::TransactionState transactionProto = mProtoParser.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