/* * 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include "FrontEnd/DisplayInfo.h" #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/Update.h" #include "LocklessStack.h" #include "TransactionProtoParser.h" #include "TransactionRingBuffer.h" using namespace android::surfaceflinger; namespace android { class SurfaceFlinger; class TransactionTracingTest; /* * 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 * pass the list of transaction ids that are committed every vsync and notify * the tracing thread. The tracing thread will then wake up and add the * committed transactions to the ring buffer. * * 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 \ < mBuffer GUARDED_BY(mTraceLock); std::unordered_map mQueuedTransactions GUARDED_BY(mTraceLock); LocklessStack mTransactionQueue; nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock); std::unordered_map mCreatedLayers GUARDED_BY(mTraceLock); std::map mStartingStates GUARDED_BY(mTraceLock); frontend::DisplayInfos mStartingDisplayInfos GUARDED_BY(mTraceLock); std::set mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock); TransactionProtoParser mProtoParser; // We do not want main thread to block so main thread will try to acquire mMainThreadLock, // otherwise will push data to temporary container. std::mutex mMainThreadLock; std::thread mThread GUARDED_BY(mMainThreadLock); bool mDone GUARDED_BY(mMainThreadLock) = false; std::condition_variable mTransactionsAvailableCv; std::condition_variable mTransactionsAddedToBufferCv; struct CommittedUpdates { std::vector transactionIds; std::vector createdLayers; std::vector destroyedLayerHandles; bool displayInfoChanged; frontend::DisplayInfos displayInfos; int64_t vsyncId; int64_t timestamp; }; std::vector mUpdates GUARDED_BY(mMainThreadLock); std::vector mPendingUpdates; // only accessed by main thread std::vector mDestroyedLayers GUARDED_BY(mMainThreadLock); std::vector 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& committedTransactions, const std::vector& removedLayers) EXCLUDES(mTraceLock); int32_t getLayerIdLocked(const sp& layerHandle) REQUIRES(mTraceLock); void tryPushToTracingThread() EXCLUDES(mMainThreadLock); std::optional createStartingStateProtoLocked() REQUIRES(mTraceLock); void updateStartingStateLocked(const perfetto::protos::TransactionTraceEntry& entry) REQUIRES(mTraceLock); }; class TransactionTraceWriter : public Singleton { friend class Singleton; std::function mWriterFunction = [](const std::string&, bool) {}; std::atomic mEnabled{true}; void doInvoke(const std::string& filename, bool overwrite) { if (mEnabled) { mWriterFunction(filename, overwrite); } }; public: void setWriterFunction( std::function function) { mWriterFunction = std::move(function); } void invoke(const std::string& prefix, bool overwrite) { doInvoke(TransactionTracing::getFilePath(prefix), overwrite); } /* pass in a complete file path for testing */ void invokeForTest(const std::string& filename, bool overwrite) { doInvoke(filename, overwrite); } /* hacky way to avoid generating traces when converting transaction trace to layers trace. */ void disable() { mEnabled.store(false); } void enable() { mEnabled.store(true); } }; } // namespace android