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