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

Commit 4c1b6469 authored by Robert Carr's avatar Robert Carr
Browse files

SurfaceFlinger: Report stuck fences to client

It's ocassionally observed in traces that the GPU is hung
resulting in fences failing to fire. This normally propagates
back up as an ANR in dequeueBuffers or onFrameAvailable. The ANR
might then pass from the app team, to the HWUI team, to the WM
team, and then if we are lucky enough to get a trace, to the
GPU team. In this CL we allow the client process to monitor
this situation itself, and proactively trigger an ANR with
a more useful and informative message than "stuck in
dequeueBuffers"

Bug: 216160569
Test: Existing tests pass
Change-Id: I3ff93bf57d24268ac57734dfed2df185985ffe1f
parent 1527529f
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -165,6 +165,17 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati
    mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
    mNumAcquired = 0;
    mNumFrameAvailable = 0;

    TransactionCompletedListener::getInstance()->addQueueStallListener(
        [&]() {
            std::function<void(bool)> callbackCopy;
            {
                std::unique_lock _lock{mMutex};
                callbackCopy = mTransactionHangCallback;
            }
            if (callbackCopy) callbackCopy(true);
        }, this);

    BQA_LOGV("BLASTBufferQueue created");
}

@@ -175,6 +186,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont
}

BLASTBufferQueue::~BLASTBufferQueue() {
    TransactionCompletedListener::getInstance()->removeQueueStallListener(this);
    if (mPendingTransactions.empty()) {
        return;
    }
@@ -1113,4 +1125,9 @@ bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceCon
    return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
}

void BLASTBufferQueue::setTransactionHangCallback(std::function<void(bool)> callback) {
    std::unique_lock _lock{mMutex};
    mTransactionHangCallback = callback;
}

} // namespace android
+9 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ namespace { // Anonymous
enum class Tag : uint32_t {
    ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
    ON_RELEASE_BUFFER,
    ON_TRANSACTION_QUEUE_STALLED,
    LAST = ON_RELEASE_BUFFER,
};

@@ -277,6 +278,11 @@ public:
                                                                  callbackId, releaseFence,
                                                                  currentMaxAcquiredBufferCount);
    }

    void onTransactionQueueStalled() override {
        callRemoteAsync<decltype(&ITransactionCompletedListener::onTransactionQueueStalled)>(
            Tag::ON_TRANSACTION_QUEUE_STALLED);
    }
};

// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -297,6 +303,9 @@ status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel&
                                  &ITransactionCompletedListener::onTransactionCompleted);
        case Tag::ON_RELEASE_BUFFER:
            return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
        case Tag::ON_TRANSACTION_QUEUE_STALLED:
            return callLocalAsync(data, reply,
                                  &ITransactionCompletedListener::onTransactionQueueStalled);
    }
}

+21 −0
Original line number Diff line number Diff line
@@ -447,6 +447,27 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
    }
}

void TransactionCompletedListener::onTransactionQueueStalled() {
      std::unordered_map<void*, std::function<void()>> callbackCopy;
      {
          std::scoped_lock<std::mutex> lock(mMutex);
          callbackCopy = mQueueStallListeners;
      }
      for (auto const& it : callbackCopy) {
          it.second();
      }
}

void TransactionCompletedListener::addQueueStallListener(std::function<void()> stallListener,
                                                         void* id) {
    std::scoped_lock<std::mutex> lock(mMutex);
    mQueueStallListeners[id] = stallListener;
}
void TransactionCompletedListener::removeQueueStallListener(void *id) {
    std::scoped_lock<std::mutex> lock(mMutex);
    mQueueStallListeners.erase(id);
}

void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
                                                   sp<Fence> releaseFence,
                                                   uint32_t currentMaxAcquiredBufferCount) {
+10 −0
Original line number Diff line number Diff line
@@ -113,6 +113,14 @@ public:
    uint64_t getLastAcquiredFrameNum();
    void abandon();

    /**
     * Set a callback to be invoked when we are hung. The boolean parameter
     * indicates whether the hang is due to an unfired fence.
     * TODO: The boolean is always true atm, unfired fence is
     * the only case we detect.
     */
    void setTransactionHangCallback(std::function<void(bool)> callback);

    virtual ~BLASTBufferQueue();

private:
@@ -269,6 +277,8 @@ private:
    // transaction that will be applied by some sync consumer.
    bool mAppliedLastTransaction = false;
    uint64_t mLastAppliedFrameNumber = 0;

    std::function<void(bool)> mTransactionHangCallback;
};

} // namespace android
+1 −0
Original line number Diff line number Diff line
@@ -194,6 +194,7 @@ public:

    virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
                                 uint32_t currentMaxAcquiredBufferCount) = 0;
    virtual void onTransactionQueueStalled() = 0;
};

class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
Loading