Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7891e96d authored by Vishnu Nair's avatar Vishnu Nair
Browse files

SF: Add continuous transaction tracing

Add the ability to capture all incoming transactions into a ring
buffer. Capture transactions as the come in through the binder
thread. On the main thread push committed transactions to the
tracing thread which will then add to the buffer and purge
older entries as the buffer gets filled.

Also introduce a new flag to enable transaction tracing (disabled
by default).

Test: presubmit
Bug: 200284593

Change-Id: I90dd0fa4f89f68cfc7b7922ecd0bba4adab8d7a4
parent 68dee2bc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -202,6 +202,7 @@ filegroup {
        "SurfaceFlingerDefaultFactory.cpp",
        "SurfaceInterceptor.cpp",
        "Tracing/LayerTracing.cpp",
        "Tracing/TransactionTracing.cpp",
        "Tracing/TransactionProtoParser.cpp",
        "TransactionCallbackInvoker.cpp",
        "TunnelModeEnabledReporter.cpp",
+47 −13
Original line number Diff line number Diff line
@@ -357,10 +357,11 @@ bool callingThreadHasRotateSurfaceFlingerAccess() {

SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
      : mFactory(factory),
        mPid(getpid()),
        mInterceptor(mFactory.createSurfaceInterceptor()),
        mTimeStats(std::make_shared<impl::TimeStats>()),
        mFrameTracer(mFactory.createFrameTracer()),
        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
        mCompositionEngine(mFactory.createCompositionEngine()),
        mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
        mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
@@ -489,6 +490,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI
    enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));

    enableLatchUnsignaledConfig = getLatchUnsignaledConfig();

    mTransactionTracingEnabled = property_get_bool("debug.sf.enable_transaction_tracing", false);
    if (mTransactionTracingEnabled) {
        mTransactionTracing.enable();
    }
}

LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -1946,7 +1952,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected
    }

    if (mTracingEnabledChanged) {
        mTracingEnabled = mLayerTracing.isEnabled();
        mLayerTracingEnabled = mLayerTracing.isEnabled();
        mTracingEnabledChanged = false;
    }

@@ -1964,7 +1970,7 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected

        bool needsTraversal = false;
        if (clearTransactionFlags(eTransactionFlushNeeded)) {
            needsTraversal = flushTransactionQueues();
            needsTraversal = flushTransactionQueues(vsyncId);
        }

        const bool shouldCommit =
@@ -2095,7 +2101,7 @@ void SurfaceFlinger::composite(nsecs_t frameTime) {
    modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);

    mLayersWithQueuedFrames.clear();
    if (mTracingEnabled) {
    if (mLayerTracingEnabled) {
        // This will block and should only be used for debugging.
        if (mVisibleRegionsDirty) {
            mLayerTracing.notify("visibleRegionsDirty");
@@ -3374,10 +3380,11 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind
        client->attachLayer(handle, lbc);
    }

    int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
    return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr,
                               InputWindowCommands{}, -1 /* desiredPresentTime */,
                               true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {},
                               0 /* Undefined transactionId */);
                               transactionId);
}

uint32_t SurfaceFlinger::getTransactionFlags() const {
@@ -3400,7 +3407,7 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule
    return old;
}

bool SurfaceFlinger::flushTransactionQueues() {
bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
    // to prevent onHandleDestroyed from being called while the lock is held,
    // we must keep a copy of the transactions (specifically the composer
    // states) around outside the scope of the lock
@@ -3486,12 +3493,13 @@ bool SurfaceFlinger::flushTransactionQueues() {
                ATRACE_INT("TransactionQueue", mTransactionQueue.size());
            }

            return applyTransactions(transactions);
            return applyTransactions(transactions, vsyncId);
        }
    }
}

bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) {
bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
                                       int64_t vsyncId) {
    bool needsTraversal = false;
    // Now apply all transactions.
    for (const auto& transaction : transactions) {
@@ -3509,6 +3517,10 @@ bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactio
                    std::move(transaction.transactionCommittedSignal));
        }
    }

    if (mTransactionTracingEnabled) {
        mTransactionTracing.addCommittedTransactions(transactions, vsyncId);
    }
    return needsTraversal;
}

@@ -3762,6 +3774,10 @@ status_t SurfaceFlinger::setTransactionState(
    state.traverseStatesWithBuffers([&](const layer_state_t& state) {
        mBufferCountTracker.increment(state.surface->localBinder());
    });

    if (mTransactionTracingEnabled) {
        mTransactionTracing.addQueuedTransaction(state);
    }
    queueTransaction(state);

    // Check the pending state to make sure the transaction is synchronous.
@@ -4448,10 +4464,12 @@ void SurfaceFlinger::onInitializeDisplays() {
    displays.add(d);

    nsecs_t now = systemTime();

    int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
    // It should be on the main thread, apply it directly.
    applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
                          /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
                          {}, getpid(), getuid(), 0 /* Undefined transactionId */);
                          {}, mPid, getuid(), transactionId);

    setPowerModeInternal(display, hal::PowerMode::ON);
    const nsecs_t vsyncPeriod =
@@ -4650,8 +4668,9 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
}

status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
    if (asProto && mLayerTracing.isEnabled()) {
    if (asProto) {
        mLayerTracing.writeToFile();
        mTransactionTracing.writeToFile();
    }

    return doDump(fd, DumpArgs(), asProto);
@@ -4848,7 +4867,6 @@ void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
}

LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
    // If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread.
    const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());

    LayersProto layersProto;
@@ -5049,6 +5067,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co
     */
    mLayerTracing.dump(result);
    result.append("\n");
    mTransactionTracing.dump(result);
    result.append("\n");

    /*
     * HWC layer minidump
@@ -5281,9 +5301,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
        code == IBinder::SYSPROPS_TRANSACTION) {
        return OK;
    }
    // Numbers from 1000 to 1040 are currently used for backdoors. The code
    // Numbers from 1000 to 1041 are currently used for backdoors. The code
    // in onTransact verifies that the user is root, and has access to use SF.
    if (code >= 1000 && code <= 1040) {
    if (code >= 1000 && code <= 1041) {
        ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
        return OK;
    }
@@ -5724,6 +5744,20 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
                scheduleRepaint();
                return NO_ERROR;
            }
            case 1041: { // Transaction tracing
                if (data.readInt32()) {
                    // Transaction tracing is always running but allow the user to temporarily
                    // increase the buffer when actively debugging.
                    mTransactionTracing.setBufferSize(
                            TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
                } else {
                    mTransactionTracing.setBufferSize(
                            TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
                    mTransactionTracing.writeToFile();
                }
                reply->writeInt32(NO_ERROR);
                return NO_ERROR;
            }
        }
    }
    return err;
+10 −5
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@
#include "SurfaceFlingerFactory.h"
#include "TracedOrdinal.h"
#include "Tracing/LayerTracing.h"
#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
#include "TransactionState.h"

@@ -711,7 +712,7 @@ private:
                               int originPid, int originUid, uint64_t transactionId)
            REQUIRES(mStateLock);
    // flush pending transaction that was presented after desiredPresentTime.
    bool flushTransactionQueues();
    bool flushTransactionQueues(int64_t vsyncId);
    // Returns true if there is at least one transaction that needs to be flushed
    bool transactionFlushNeeded();

@@ -747,7 +748,8 @@ private:
    bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock);
    bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction)
            REQUIRES(mStateLock);
    bool applyTransactions(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
    bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId)
            REQUIRES(mStateLock);
    uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
    uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
            REQUIRES(mStateLock);
@@ -1082,7 +1084,7 @@ private:

    sp<StartPropertySetThread> mStartPropertySetThread;
    surfaceflinger::Factory& mFactory;

    pid_t mPid;
    std::future<void> mRenderEnginePrimeCacheFuture;

    // access must be protected by mStateLock
@@ -1091,6 +1093,7 @@ private:
    std::atomic<int32_t> mTransactionFlags = 0;
    std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
    bool mAnimTransactionPending = false;
    std::atomic<uint32_t> mUniqueTransactionId = 1;
    SortedVector<sp<Layer>> mLayersPendingRemoval;

    // global color transform states
@@ -1181,8 +1184,10 @@ private:
    sp<SurfaceInterceptor> mInterceptor;

    LayerTracing mLayerTracing{*this};
    std::mutex mTracingLock;
    bool mTracingEnabled = false;
    bool mLayerTracingEnabled = false;

    TransactionTracing mTransactionTracing;
    bool mTransactionTracingEnabled = false;
    std::atomic<bool> mTracingEnabledChanged = false;

    const std::shared_ptr<TimeStats> mTimeStats;
+12 −6
Original line number Diff line number Diff line
@@ -21,8 +21,9 @@

#include <log/log.h>
#include <utils/Errors.h>
#include <utils/SystemClock.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
#include <chrono>
#include <queue>

namespace android {
@@ -57,9 +58,7 @@ public:
    status_t writeToFile(FileProto& fileProto, std::string filename) {
        ATRACE_CALL();
        std::string output;
        flush(fileProto);
        reset(mSizeInBytes);
        if (!fileProto.SerializeToString(&output)) {
        if (!writeToString(fileProto, &output)) {
            ALOGE("Could not serialize proto.");
            return UNKNOWN_ERROR;
        }
@@ -73,6 +72,13 @@ public:
        return NO_ERROR;
    }

    bool writeToString(FileProto& fileProto, std::string* outString) {
        ATRACE_CALL();
        flush(fileProto);
        reset(mSizeInBytes);
        return fileProto.SerializeToString(outString);
    }

    std::vector<EntryProto> emplace(EntryProto&& proto) {
        std::vector<EntryProto> replacedEntries;
        size_t protoSize = static_cast<size_t>(proto.ByteSize());
@@ -99,8 +105,8 @@ public:
        const int64_t durationCount = duration.count();
        base::StringAppendF(&result,
                            "  number of entries: %zu (%.2fMB / %.2fMB) duration: %" PRIi64 "ms\n",
                            frameCount(), float(used()) / 1024.f * 1024.f,
                            float(size()) / 1024.f * 1024.f, durationCount);
                            frameCount(), float(used()) / (1024.f * 1024.f),
                            float(size()) / (1024.f * 1024.f), durationCount);
    }

private:
+203 −0
Original line number Diff line number Diff line
/*
 * 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 "RingBuffer.h"
#include "TransactionTracing.h"

namespace android {

TransactionTracing::TransactionTracing() {
    mBuffer = std::make_unique<
            RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>();
}

TransactionTracing::~TransactionTracing() = default;

bool TransactionTracing::enable() {
    std::scoped_lock lock(mTraceLock);
    if (mEnabled) {
        return false;
    }
    mBuffer->setSize(mBufferSizeInBytes);
    mEnabled = true;
    {
        std::scoped_lock lock(mMainThreadLock);
        mDone = false;
        mThread = std::thread(&TransactionTracing::loop, this);
    }
    return true;
}

bool TransactionTracing::disable() {
    std::thread thread;
    {
        std::scoped_lock lock(mMainThreadLock);
        mDone = true;
        mTransactionsAvailableCv.notify_all();
        thread = std::move(mThread);
    }
    if (thread.joinable()) {
        thread.join();
    }

    std::scoped_lock lock(mTraceLock);
    if (!mEnabled) {
        return false;
    }
    mEnabled = false;

    proto::TransactionTraceFile fileProto = createTraceFileProto();
    mBuffer->writeToFile(fileProto, FILE_NAME);
    mQueuedTransactions.clear();
    return true;
}

bool TransactionTracing::isEnabled() const {
    std::scoped_lock lock(mTraceLock);
    return mEnabled;
}

status_t TransactionTracing::writeToFile() {
    std::scoped_lock lock(mTraceLock);
    if (!mEnabled) {
        return STATUS_OK;
    }
    proto::TransactionTraceFile fileProto = createTraceFileProto();
    return mBuffer->writeToFile(fileProto, FILE_NAME);
}

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, "Transaction tracing state: %s\n",
                        mEnabled ? "enabled" : "disabled");
    base::StringAppendF(&result, "  queued transactions: %d\n",
                        static_cast<uint32_t>(mQueuedTransactions.size()));
    mBuffer->dump(result);
}

void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
    std::scoped_lock lock(mTraceLock);
    ATRACE_CALL();
    if (!mEnabled) {
        return;
    }
    mQueuedTransactions[transaction.id] =
            TransactionProtoParser::toProto(transaction, nullptr, nullptr);
}

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);
    }

    // Try to acquire the lock from main thread, but don't block if we cannot acquire the lock. Add
    // it to pending transactions that we can collect later.
    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();
        mCommittedTransactions.emplace_back(committedTransactions);
        mTransactionsAvailableCv.notify_one();
        mMainThreadLock.unlock();
    } else {
        mPendingTransactions.emplace_back(committedTransactions);
    }
}

void TransactionTracing::loop() {
    while (true) {
        std::vector<CommittedTransactions> committedTransactions;
        {
            std::unique_lock<std::mutex> lock(mMainThreadLock);
            base::ScopedLockAssertion assumeLocked(mMainThreadLock);
            mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
                return mDone || !mCommittedTransactions.empty();
            });
            if (mDone) {
                mCommittedTransactions.clear();
                break;
            }
            committedTransactions = std::move(mCommittedTransactions);
            mCommittedTransactions.clear();
        } // unlock mMainThreadLock

        addEntry(committedTransactions);

        mTransactionsAddedToBufferCv.notify_one();
    }
}

void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions) {
    ATRACE_CALL();
    std::scoped_lock lock(mTraceLock);
    std::vector<proto::TransactionTraceEntry> removedEntries;
    for (const CommittedTransactions& entry : committedTransactions) {
        proto::TransactionTraceEntry entryProto;
        entryProto.set_elapsed_realtime_nanos(entry.timestamp);
        entryProto.set_vsync_id(entry.vsyncId);
        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 {
                ALOGE("Could not find transaction id %" PRIu64, id);
            }
        }
        mBuffer->emplace(std::move(entryProto));
    }
}

void TransactionTracing::flush() {
    std::unique_lock<std::mutex> lock(mMainThreadLock);
    base::ScopedLockAssertion assumeLocked(mMainThreadLock);
    mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
        return mCommittedTransactions.empty();
    });
}

} // namespace android
Loading