• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #undef LOG_TAG
18 #define LOG_TAG "TransactionTracing"
19 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
20 
21 #include <android-base/stringprintf.h>
22 #include <log/log.h>
23 #include <utils/SystemClock.h>
24 #include <utils/Trace.h>
25 
26 #include "Client.h"
27 #include "FrontEnd/LayerCreationArgs.h"
28 #include "TransactionTracing.h"
29 
30 namespace android {
ANDROID_SINGLETON_STATIC_INSTANCE(android::TransactionTraceWriter)31 ANDROID_SINGLETON_STATIC_INSTANCE(android::TransactionTraceWriter)
32 
33 TransactionTracing::TransactionTracing()
34       : mProtoParser(std::make_unique<TransactionProtoParser::FlingerDataMapper>()) {
35     std::scoped_lock lock(mTraceLock);
36 
37     mBuffer.setSize(mBufferSizeInBytes);
38     mStartingTimestamp = systemTime();
39     {
40         std::scoped_lock lock(mMainThreadLock);
41         mThread = std::thread(&TransactionTracing::loop, this);
42     }
43 }
44 
~TransactionTracing()45 TransactionTracing::~TransactionTracing() {
46     std::thread thread;
47     {
48         std::scoped_lock lock(mMainThreadLock);
49         mDone = true;
50         mTransactionsAvailableCv.notify_all();
51         thread = std::move(mThread);
52     }
53     if (thread.joinable()) {
54         thread.join();
55     }
56 
57     writeToFile();
58 }
59 
writeToFile(const std::string & filename)60 status_t TransactionTracing::writeToFile(const std::string& filename) {
61     std::scoped_lock lock(mTraceLock);
62     proto::TransactionTraceFile fileProto = createTraceFileProto();
63     addStartingStateToProtoLocked(fileProto);
64     return mBuffer.writeToFile(fileProto, filename);
65 }
66 
setBufferSize(size_t bufferSizeInBytes)67 void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
68     std::scoped_lock lock(mTraceLock);
69     mBufferSizeInBytes = bufferSizeInBytes;
70     mBuffer.setSize(mBufferSizeInBytes);
71 }
72 
createTraceFileProto() const73 proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
74     proto::TransactionTraceFile proto;
75     proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
76                            proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
77     auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) -
78                                                    systemTime(SYSTEM_TIME_MONOTONIC));
79     proto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs);
80     proto.set_version(TRACING_VERSION);
81     return proto;
82 }
83 
dump(std::string & result) const84 void TransactionTracing::dump(std::string& result) const {
85     std::scoped_lock lock(mTraceLock);
86     base::StringAppendF(&result, "  queued transactions=%zu created layers=%zu states=%zu\n",
87                         mQueuedTransactions.size(), mCreatedLayers.size(), mStartingStates.size());
88     mBuffer.dump(result);
89 }
90 
addQueuedTransaction(const TransactionState & transaction)91 void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
92     proto::TransactionState* state = new proto::TransactionState(mProtoParser.toProto(transaction));
93     mTransactionQueue.push(state);
94 }
95 
addCommittedTransactions(int64_t vsyncId,nsecs_t commitTime,frontend::Update & newUpdate,const frontend::DisplayInfos & displayInfos,bool displayInfoChanged)96 void TransactionTracing::addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime,
97                                                   frontend::Update& newUpdate,
98                                                   const frontend::DisplayInfos& displayInfos,
99                                                   bool displayInfoChanged) {
100     CommittedUpdates update;
101     update.vsyncId = vsyncId;
102     update.timestamp = commitTime;
103     update.transactionIds.reserve(newUpdate.transactions.size());
104     for (const auto& transaction : newUpdate.transactions) {
105         update.transactionIds.emplace_back(transaction.id);
106     }
107     update.displayInfoChanged = displayInfoChanged;
108     if (displayInfoChanged) {
109         update.displayInfos = displayInfos;
110     }
111     update.createdLayers = std::move(newUpdate.layerCreationArgs);
112     newUpdate.layerCreationArgs.clear();
113     update.destroyedLayerHandles.reserve(newUpdate.destroyedHandles.size());
114     for (uint32_t handle : newUpdate.destroyedHandles) {
115         update.destroyedLayerHandles.push_back(handle);
116     }
117     mPendingUpdates.emplace_back(update);
118     tryPushToTracingThread();
119     mLastUpdatedVsyncId = vsyncId;
120 }
121 
loop()122 void TransactionTracing::loop() {
123     while (true) {
124         std::vector<CommittedUpdates> committedUpdates;
125         std::vector<uint32_t> destroyedLayers;
126         {
127             std::unique_lock<std::mutex> lock(mMainThreadLock);
128             base::ScopedLockAssertion assumeLocked(mMainThreadLock);
129             mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
130                 return mDone || !mUpdates.empty();
131             });
132             if (mDone) {
133                 mUpdates.clear();
134                 mDestroyedLayers.clear();
135                 break;
136             }
137 
138             destroyedLayers = std::move(mDestroyedLayers);
139             mDestroyedLayers.clear();
140             committedUpdates = std::move(mUpdates);
141             mUpdates.clear();
142         } // unlock mMainThreadLock
143 
144         if (!committedUpdates.empty() || !destroyedLayers.empty()) {
145             addEntry(committedUpdates, destroyedLayers);
146         }
147     }
148 }
149 
addEntry(const std::vector<CommittedUpdates> & committedUpdates,const std::vector<uint32_t> & destroyedLayers)150 void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committedUpdates,
151                                   const std::vector<uint32_t>& destroyedLayers) {
152     ATRACE_CALL();
153     std::scoped_lock lock(mTraceLock);
154     std::vector<std::string> removedEntries;
155     proto::TransactionTraceEntry entryProto;
156 
157     while (auto incomingTransaction = mTransactionQueue.pop()) {
158         auto transaction = *incomingTransaction;
159         mQueuedTransactions[incomingTransaction->transaction_id()] = transaction;
160         delete incomingTransaction;
161     }
162     for (const CommittedUpdates& update : committedUpdates) {
163         entryProto.set_elapsed_realtime_nanos(update.timestamp);
164         entryProto.set_vsync_id(update.vsyncId);
165         entryProto.mutable_added_layers()->Reserve(
166                 static_cast<int32_t>(update.createdLayers.size()));
167 
168         for (const auto& args : update.createdLayers) {
169             entryProto.mutable_added_layers()->Add(std::move(mProtoParser.toProto(args)));
170         }
171 
172         entryProto.mutable_destroyed_layers()->Reserve(
173                 static_cast<int32_t>(destroyedLayers.size()));
174         for (auto& destroyedLayer : destroyedLayers) {
175             entryProto.mutable_destroyed_layers()->Add(destroyedLayer);
176         }
177         entryProto.mutable_transactions()->Reserve(
178                 static_cast<int32_t>(update.transactionIds.size()));
179         for (const uint64_t& id : update.transactionIds) {
180             auto it = mQueuedTransactions.find(id);
181             if (it != mQueuedTransactions.end()) {
182                 entryProto.mutable_transactions()->Add(std::move(it->second));
183                 mQueuedTransactions.erase(it);
184             } else {
185                 ALOGW("Could not find transaction id %" PRIu64, id);
186             }
187         }
188 
189         entryProto.mutable_destroyed_layer_handles()->Reserve(
190                 static_cast<int32_t>(update.destroyedLayerHandles.size()));
191         for (auto layerId : update.destroyedLayerHandles) {
192             entryProto.mutable_destroyed_layer_handles()->Add(layerId);
193         }
194 
195         entryProto.set_displays_changed(update.displayInfoChanged);
196         if (update.displayInfoChanged) {
197             entryProto.mutable_displays()->Reserve(
198                     static_cast<int32_t>(update.displayInfos.size()));
199             for (auto& [layerStack, displayInfo] : update.displayInfos) {
200                 entryProto.mutable_displays()->Add(
201                         std::move(mProtoParser.toProto(displayInfo, layerStack.id)));
202             }
203         }
204 
205         std::string serializedProto;
206         entryProto.SerializeToString(&serializedProto);
207         entryProto.Clear();
208         std::vector<std::string> entries = mBuffer.emplace(std::move(serializedProto));
209         removedEntries.reserve(removedEntries.size() + entries.size());
210         removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
211                               std::make_move_iterator(entries.end()));
212     }
213 
214     proto::TransactionTraceEntry removedEntryProto;
215     for (const std::string& removedEntry : removedEntries) {
216         removedEntryProto.ParseFromString(removedEntry);
217         updateStartingStateLocked(removedEntryProto);
218         removedEntryProto.Clear();
219     }
220     mTransactionsAddedToBufferCv.notify_one();
221 }
222 
flush()223 void TransactionTracing::flush() {
224     {
225         std::scoped_lock lock(mMainThreadLock);
226         // Collect any pending transactions and wait for transactions to be added to
227         mUpdates.insert(mUpdates.end(), std::make_move_iterator(mPendingUpdates.begin()),
228                         std::make_move_iterator(mPendingUpdates.end()));
229         mPendingUpdates.clear();
230         mDestroyedLayers.insert(mDestroyedLayers.end(), mPendingDestroyedLayers.begin(),
231                                 mPendingDestroyedLayers.end());
232         mPendingDestroyedLayers.clear();
233         mTransactionsAvailableCv.notify_one();
234     }
235     std::unique_lock<std::mutex> lock(mTraceLock);
236     base::ScopedLockAssertion assumeLocked(mTraceLock);
237     mTransactionsAddedToBufferCv.wait_for(lock, std::chrono::milliseconds(100),
238                                           [&]() REQUIRES(mTraceLock) {
239                                               proto::TransactionTraceEntry entry;
240                                               if (mBuffer.used() > 0) {
241                                                   entry.ParseFromString(mBuffer.back());
242                                               }
243                                               return mBuffer.used() > 0 &&
244                                                       entry.vsync_id() >= mLastUpdatedVsyncId;
245                                           });
246 }
247 
onLayerRemoved(int32_t layerId)248 void TransactionTracing::onLayerRemoved(int32_t layerId) {
249     mPendingDestroyedLayers.emplace_back(layerId);
250     tryPushToTracingThread();
251 }
252 
tryPushToTracingThread()253 void TransactionTracing::tryPushToTracingThread() {
254     // Try to acquire the lock from main thread.
255     if (mMainThreadLock.try_lock()) {
256         // We got the lock! Collect any pending transactions and continue.
257         mUpdates.insert(mUpdates.end(), std::make_move_iterator(mPendingUpdates.begin()),
258                         std::make_move_iterator(mPendingUpdates.end()));
259         mPendingUpdates.clear();
260         mDestroyedLayers.insert(mDestroyedLayers.end(), mPendingDestroyedLayers.begin(),
261                                 mPendingDestroyedLayers.end());
262         mPendingDestroyedLayers.clear();
263         mTransactionsAvailableCv.notify_one();
264         mMainThreadLock.unlock();
265     } else {
266         ALOGV("Couldn't get lock");
267     }
268 }
269 
updateStartingStateLocked(const proto::TransactionTraceEntry & removedEntry)270 void TransactionTracing::updateStartingStateLocked(
271         const proto::TransactionTraceEntry& removedEntry) {
272     mStartingTimestamp = removedEntry.elapsed_realtime_nanos();
273     // Keep track of layer starting state so we can reconstruct the layer state as we purge
274     // transactions from the buffer.
275     for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
276         TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
277         startingState.layerId = addedLayer.layer_id();
278         mProtoParser.fromProto(addedLayer, startingState.args);
279     }
280 
281     // Merge layer states to starting transaction state.
282     for (const proto::TransactionState& transaction : removedEntry.transactions()) {
283         for (const proto::LayerState& layerState : transaction.layer_changes()) {
284             auto it = mStartingStates.find(layerState.layer_id());
285             if (it == mStartingStates.end()) {
286                 // TODO(b/238781169) make this log fatal when we switch over to using new fe
287                 ALOGW("Could not find layer id %d", layerState.layer_id());
288                 continue;
289             }
290             mProtoParser.mergeFromProto(layerState, it->second);
291         }
292     }
293 
294     for (const uint32_t destroyedLayerHandleId : removedEntry.destroyed_layer_handles()) {
295         mRemovedLayerHandlesAtStart.insert(destroyedLayerHandleId);
296     }
297 
298     // Clean up stale starting states since the layer has been removed and the buffer does not
299     // contain any references to the layer.
300     for (const uint32_t destroyedLayerId : removedEntry.destroyed_layers()) {
301         mStartingStates.erase(destroyedLayerId);
302         mRemovedLayerHandlesAtStart.erase(destroyedLayerId);
303     }
304 
305     if (removedEntry.displays_changed()) {
306         mProtoParser.fromProto(removedEntry.displays(), mStartingDisplayInfos);
307     }
308 }
309 
addStartingStateToProtoLocked(proto::TransactionTraceFile & proto)310 void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
311     if (mStartingStates.size() == 0) {
312         return;
313     }
314 
315     proto::TransactionTraceEntry* entryProto = proto.add_entry();
316     entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
317     entryProto->set_vsync_id(0);
318 
319     entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
320     for (auto& [layerId, state] : mStartingStates) {
321         entryProto->mutable_added_layers()->Add(mProtoParser.toProto(state.args));
322     }
323 
324     proto::TransactionState transactionProto = mProtoParser.toProto(mStartingStates);
325     transactionProto.set_vsync_id(0);
326     transactionProto.set_post_time(mStartingTimestamp);
327     entryProto->mutable_transactions()->Add(std::move(transactionProto));
328 
329     entryProto->mutable_destroyed_layer_handles()->Reserve(
330             static_cast<int32_t>(mRemovedLayerHandlesAtStart.size()));
331     for (const uint32_t destroyedLayerHandleId : mRemovedLayerHandlesAtStart) {
332         entryProto->mutable_destroyed_layer_handles()->Add(destroyedLayerHandleId);
333     }
334 
335     entryProto->mutable_displays()->Reserve(static_cast<int32_t>(mStartingDisplayInfos.size()));
336     for (auto& [layerStack, displayInfo] : mStartingDisplayInfos) {
337         entryProto->mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
338     }
339 }
340 
writeToProto()341 proto::TransactionTraceFile TransactionTracing::writeToProto() {
342     std::scoped_lock<std::mutex> lock(mTraceLock);
343     proto::TransactionTraceFile proto = createTraceFileProto();
344     addStartingStateToProtoLocked(proto);
345     mBuffer.writeToProto(proto);
346     return proto;
347 }
348 
349 } // namespace android
350