blob: cf488c26f9d1d22b69444bd7825532b13d62156c [file] [log] [blame]
Vishnu Nair7891e962021-11-11 12:07:21 -08001/*
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 "RingBuffer.h"
27#include "TransactionTracing.h"
28
29namespace android {
30
31TransactionTracing::TransactionTracing() {
32 mBuffer = std::make_unique<
33 RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>();
34}
35
36TransactionTracing::~TransactionTracing() = default;
37
38bool TransactionTracing::enable() {
39 std::scoped_lock lock(mTraceLock);
40 if (mEnabled) {
41 return false;
42 }
43 mBuffer->setSize(mBufferSizeInBytes);
Vishnu Nair0cc69e12021-11-18 09:05:49 -080044 mStartingTimestamp = systemTime();
Vishnu Nair7891e962021-11-11 12:07:21 -080045 mEnabled = true;
46 {
47 std::scoped_lock lock(mMainThreadLock);
48 mDone = false;
49 mThread = std::thread(&TransactionTracing::loop, this);
50 }
51 return true;
52}
53
54bool TransactionTracing::disable() {
55 std::thread thread;
56 {
57 std::scoped_lock lock(mMainThreadLock);
58 mDone = true;
59 mTransactionsAvailableCv.notify_all();
60 thread = std::move(mThread);
61 }
62 if (thread.joinable()) {
63 thread.join();
64 }
65
66 std::scoped_lock lock(mTraceLock);
67 if (!mEnabled) {
68 return false;
69 }
70 mEnabled = false;
71
Vishnu Nair0cc69e12021-11-18 09:05:49 -080072 writeToFileLocked();
73 mBuffer->reset();
Vishnu Nair7891e962021-11-11 12:07:21 -080074 mQueuedTransactions.clear();
Vishnu Nair0cc69e12021-11-18 09:05:49 -080075 mStartingStates.clear();
76 mLayerHandles.clear();
Vishnu Nair7891e962021-11-11 12:07:21 -080077 return true;
78}
79
80bool TransactionTracing::isEnabled() const {
81 std::scoped_lock lock(mTraceLock);
82 return mEnabled;
83}
84
85status_t TransactionTracing::writeToFile() {
86 std::scoped_lock lock(mTraceLock);
87 if (!mEnabled) {
88 return STATUS_OK;
89 }
Vishnu Nair0cc69e12021-11-18 09:05:49 -080090 return writeToFileLocked();
91}
92
93status_t TransactionTracing::writeToFileLocked() {
Vishnu Nair7891e962021-11-11 12:07:21 -080094 proto::TransactionTraceFile fileProto = createTraceFileProto();
Vishnu Nair0cc69e12021-11-18 09:05:49 -080095 addStartingStateToProtoLocked(fileProto);
Vishnu Nair7891e962021-11-11 12:07:21 -080096 return mBuffer->writeToFile(fileProto, FILE_NAME);
97}
98
99void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
100 std::scoped_lock lock(mTraceLock);
101 mBufferSizeInBytes = bufferSizeInBytes;
102 mBuffer->setSize(mBufferSizeInBytes);
103}
104
105proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
106 proto::TransactionTraceFile proto;
107 proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
108 proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
109 return proto;
110}
111
112void TransactionTracing::dump(std::string& result) const {
113 std::scoped_lock lock(mTraceLock);
114 base::StringAppendF(&result, "Transaction tracing state: %s\n",
115 mEnabled ? "enabled" : "disabled");
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800116 base::StringAppendF(&result,
117 " queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
118 mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
119 mStartingStates.size());
Vishnu Nair7891e962021-11-11 12:07:21 -0800120 mBuffer->dump(result);
121}
122
123void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
124 std::scoped_lock lock(mTraceLock);
125 ATRACE_CALL();
126 if (!mEnabled) {
127 return;
128 }
129 mQueuedTransactions[transaction.id] =
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800130 TransactionProtoParser::toProto(transaction,
131 std::bind(&TransactionTracing::getLayerIdLocked, this,
132 std::placeholders::_1),
133 nullptr);
Vishnu Nair7891e962021-11-11 12:07:21 -0800134}
135
136void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
137 int64_t vsyncId) {
138 CommittedTransactions committedTransactions;
139 committedTransactions.vsyncId = vsyncId;
140 committedTransactions.timestamp = systemTime();
141 committedTransactions.transactionIds.reserve(transactions.size());
142 for (const auto& transaction : transactions) {
143 committedTransactions.transactionIds.emplace_back(transaction.id);
144 }
145
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800146 mPendingTransactions.emplace_back(committedTransactions);
147 tryPushToTracingThread();
Vishnu Nair7891e962021-11-11 12:07:21 -0800148}
149
150void TransactionTracing::loop() {
151 while (true) {
152 std::vector<CommittedTransactions> committedTransactions;
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800153 std::vector<int32_t> removedLayers;
Vishnu Nair7891e962021-11-11 12:07:21 -0800154 {
155 std::unique_lock<std::mutex> lock(mMainThreadLock);
156 base::ScopedLockAssertion assumeLocked(mMainThreadLock);
157 mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
158 return mDone || !mCommittedTransactions.empty();
159 });
160 if (mDone) {
161 mCommittedTransactions.clear();
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800162 mRemovedLayers.clear();
Vishnu Nair7891e962021-11-11 12:07:21 -0800163 break;
164 }
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800165
166 removedLayers = std::move(mRemovedLayers);
167 mRemovedLayers.clear();
Vishnu Nair7891e962021-11-11 12:07:21 -0800168 committedTransactions = std::move(mCommittedTransactions);
169 mCommittedTransactions.clear();
170 } // unlock mMainThreadLock
171
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800172 addEntry(committedTransactions, removedLayers);
Vishnu Nair7891e962021-11-11 12:07:21 -0800173 }
174}
175
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800176void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
177 const std::vector<int32_t>& removedLayers) {
Vishnu Nair7891e962021-11-11 12:07:21 -0800178 ATRACE_CALL();
179 std::scoped_lock lock(mTraceLock);
180 std::vector<proto::TransactionTraceEntry> removedEntries;
181 for (const CommittedTransactions& entry : committedTransactions) {
182 proto::TransactionTraceEntry entryProto;
183 entryProto.set_elapsed_realtime_nanos(entry.timestamp);
184 entryProto.set_vsync_id(entry.vsyncId);
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800185 entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size()));
186 for (auto& newLayer : mCreatedLayers) {
187 entryProto.mutable_added_layers()->Add(std::move(newLayer));
188 }
189 entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
190 for (auto& removedLayer : removedLayers) {
191 entryProto.mutable_removed_layers()->Add(removedLayer);
192 }
193 mCreatedLayers.clear();
Vishnu Nair7891e962021-11-11 12:07:21 -0800194 entryProto.mutable_transactions()->Reserve(
195 static_cast<int32_t>(entry.transactionIds.size()));
196 for (const uint64_t& id : entry.transactionIds) {
197 auto it = mQueuedTransactions.find(id);
198 if (it != mQueuedTransactions.end()) {
199 entryProto.mutable_transactions()->Add(std::move(it->second));
200 mQueuedTransactions.erase(it);
201 } else {
202 ALOGE("Could not find transaction id %" PRIu64, id);
203 }
204 }
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800205 std::vector<proto::TransactionTraceEntry> entries = mBuffer->emplace(std::move(entryProto));
206 removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
207 std::make_move_iterator(entries.end()));
208 }
209
210 for (const proto::TransactionTraceEntry& removedEntry : removedEntries) {
211 updateStartingStateLocked(removedEntry);
212 }
213 mTransactionsAddedToBufferCv.notify_one();
214}
215
216void TransactionTracing::flush(int64_t vsyncId) {
217 while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
218 tryPushToTracingThread();
219 }
220 std::unique_lock<std::mutex> lock(mTraceLock);
221 base::ScopedLockAssertion assumeLocked(mTraceLock);
222 mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
223 return mBuffer->used() > 0 && mBuffer->back().vsync_id() >= vsyncId;
224 });
225}
226
227void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
228 uint32_t flags, int parentId) {
229 std::scoped_lock lock(mTraceLock);
230 TracingLayerCreationArgs args{layerId, name, flags, parentId};
231 mLayerHandles[layerHandle] = layerId;
232 mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
233}
234
235void TransactionTracing::onLayerRemoved(int32_t layerId) {
236 mPendingRemovedLayers.emplace_back(layerId);
237 tryPushToTracingThread();
238}
239
240void TransactionTracing::tryPushToTracingThread() {
241 // Try to acquire the lock from main thread.
242 if (mMainThreadLock.try_lock()) {
243 // We got the lock! Collect any pending transactions and continue.
244 mCommittedTransactions.insert(mCommittedTransactions.end(),
245 std::make_move_iterator(mPendingTransactions.begin()),
246 std::make_move_iterator(mPendingTransactions.end()));
247 mPendingTransactions.clear();
248 mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
249 mPendingRemovedLayers.end());
250 mPendingRemovedLayers.clear();
251 mTransactionsAvailableCv.notify_one();
252 mMainThreadLock.unlock();
253 } else {
254 ALOGV("Couldn't get lock");
Vishnu Nair7891e962021-11-11 12:07:21 -0800255 }
256}
257
Vishnu Nair0cc69e12021-11-18 09:05:49 -0800258int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) {
259 if (layerHandle == nullptr) {
260 return -1;
261 }
262 auto it = mLayerHandles.find(layerHandle->localBinder());
263 return it == mLayerHandles.end() ? -1 : it->second;
264}
265
266void TransactionTracing::updateStartingStateLocked(
267 const proto::TransactionTraceEntry& removedEntry) {
268 // Keep track of layer starting state so we can reconstruct the layer state as we purge
269 // transactions from the buffer.
270 for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
271 TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
272 startingState.layerId = addedLayer.layer_id();
273 startingState.name = addedLayer.name();
274 startingState.layerCreationFlags = addedLayer.flags();
275 startingState.parentId = addedLayer.parent_id();
276 }
277
278 // Merge layer states to starting transaction state.
279 for (const proto::TransactionState& transaction : removedEntry.transactions()) {
280 for (const proto::LayerState& layerState : transaction.layer_changes()) {
281 auto it = mStartingStates.find(layerState.layer_id());
282 if (it == mStartingStates.end()) {
283 ALOGE("Could not find layer id %d", layerState.layer_id());
284 continue;
285 }
286 TransactionProtoParser::fromProto(layerState, nullptr, it->second);
287 }
288 }
289
290 // Clean up stale starting states since the layer has been removed and the buffer does not
291 // contain any references to the layer.
292 for (const int32_t removedLayerId : removedEntry.removed_layers()) {
293 auto it = std::find_if(mLayerHandles.begin(), mLayerHandles.end(),
294 [removedLayerId](auto& layer) {
295 return layer.second == removedLayerId;
296 });
297 if (it != mLayerHandles.end()) {
298 mLayerHandles.erase(it);
299 }
300 mStartingStates.erase(removedLayerId);
301 }
302}
303
304void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
305 proto::TransactionTraceEntry* entryProto = proto.add_entry();
306 entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
307 entryProto->set_vsync_id(0);
308 entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
309 for (auto& [layerId, state] : mStartingStates) {
310 TracingLayerCreationArgs args{layerId, state.name, state.layerCreationFlags,
311 state.parentId};
312 entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(args));
313 }
314
315 proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates);
316 transactionProto.set_vsync_id(0);
317 transactionProto.set_post_time(mStartingTimestamp);
318 entryProto->mutable_transactions()->Add(std::move(transactionProto));
319}
320
321proto::TransactionTraceFile TransactionTracing::writeToProto() {
322 std::scoped_lock<std::mutex> lock(mTraceLock);
323 proto::TransactionTraceFile proto = createTraceFileProto();
324 addStartingStateToProtoLocked(proto);
325 mBuffer->writeToProto(proto);
326 return proto;
Vishnu Nair7891e962021-11-11 12:07:21 -0800327}
328
329} // namespace android