diff options
| -rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.cpp | 372 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.h | 55 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 228 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 72 | ||||
| -rw-r--r-- | services/surfaceflinger/TimeStats/TimeStats.cpp | 11 | ||||
| -rw-r--r-- | services/surfaceflinger/TimeStats/TimeStats.h | 2 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/TimeStatsTest.cpp | 3 |
7 files changed, 347 insertions, 396 deletions
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 7b25adb73f..281f6b7069 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -19,8 +19,11 @@ #include <pthread.h> #include <sched.h> #include <sys/types.h> + #include <chrono> #include <cstdint> +#include <optional> +#include <type_traits> #include <android-base/stringprintf.h> @@ -35,18 +38,66 @@ #include "EventThread.h" using namespace std::chrono_literals; -using android::base::StringAppendF; - -// --------------------------------------------------------------------------- namespace android { -// --------------------------------------------------------------------------- +using base::StringAppendF; +using base::StringPrintf; + +namespace { + +auto vsyncPeriod(VSyncRequest request) { + return static_cast<std::underlying_type_t<VSyncRequest>>(request); +} + +std::string toString(VSyncRequest request) { + switch (request) { + case VSyncRequest::None: + return "VSyncRequest::None"; + case VSyncRequest::Single: + return "VSyncRequest::Single"; + default: + return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request)); + } +} + +std::string toString(const EventThreadConnection& connection) { + return StringPrintf("Connection{%p, %s}", &connection, + toString(connection.vsyncRequest).c_str()); +} + +std::string toString(const DisplayEventReceiver::Event& event) { + switch (event.header.type) { + case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: + return StringPrintf("Hotplug{displayId=%u, %s}", event.header.id, + event.hotplug.connected ? "connected" : "disconnected"); + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + return StringPrintf("VSync{displayId=%u, count=%u}", event.header.id, + event.vsync.count); + default: + return "Event{}"; + } +} + +DisplayEventReceiver::Event makeHotplug(uint32_t displayId, nsecs_t timestamp, bool connected) { + DisplayEventReceiver::Event event; + event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp}; + event.hotplug.connected = connected; + return event; +} + +DisplayEventReceiver::Event makeVSync(uint32_t displayId, nsecs_t timestamp, uint32_t count) { + DisplayEventReceiver::Event event; + event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp}; + event.vsync.count = count; + return event; +} + +} // namespace EventThreadConnection::EventThreadConnection(EventThread* eventThread, ResyncCallback resyncCallback) : resyncCallback(std::move(resyncCallback)), - count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {} @@ -65,8 +116,8 @@ status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) { return NO_ERROR; } -status_t EventThreadConnection::setVsyncRate(uint32_t count) { - mEventThread->setVsyncRate(count, this); +status_t EventThreadConnection::setVsyncRate(uint32_t rate) { + mEventThread->setVsyncRate(rate, this); return NO_ERROR; } @@ -107,18 +158,16 @@ EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSr InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) : mVSyncSource(src), mVSyncSourceUnique(std::move(uniqueSrc)), - mInterceptVSyncsCallback(interceptVSyncsCallback) { + mInterceptVSyncsCallback(interceptVSyncsCallback), + mThreadName(threadName) { if (src == nullptr) { mVSyncSource = mVSyncSourceUnique.get(); } - for (auto& event : mVSyncEvent) { - event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - event.header.id = 0; - event.header.timestamp = 0; - event.vsync.count = 0; - } - mThread = std::thread(&EventThread::threadMain, this); + mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS { + std::unique_lock<std::mutex> lock(mMutex); + threadMain(lock); + }); pthread_setname_np(mThread.native_handle(), threadName); @@ -178,14 +227,17 @@ void EventThread::removeDisplayEventConnectionLocked(const wp<EventThreadConnect } } -void EventThread::setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) { - if (int32_t(count) >= 0) { // server must protect against bad params - std::lock_guard<std::mutex> lock(mMutex); - const int32_t new_count = (count == 0) ? -1 : count; - if (connection->count != new_count) { - connection->count = new_count; - mCondition.notify_all(); - } +void EventThread::setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) { + if (static_cast<std::underlying_type_t<VSyncRequest>>(rate) < 0) { + return; + } + + std::lock_guard<std::mutex> lock(mMutex); + + const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate); + if (connection->vsyncRequest != request) { + connection->vsyncRequest = request; + mCondition.notify_all(); } } @@ -201,164 +253,91 @@ void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection, std::lock_guard<std::mutex> lock(mMutex); - if (connection->count < 0) { - connection->count = 0; + if (connection->vsyncRequest == VSyncRequest::None) { + connection->vsyncRequest = VSyncRequest::Single; mCondition.notify_all(); } } void EventThread::onScreenReleased() { std::lock_guard<std::mutex> lock(mMutex); - if (!mUseSoftwareVSync) { - // disable reliance on h/w vsync - mUseSoftwareVSync = true; + if (!mVSyncState.synthetic) { + mVSyncState.synthetic = true; mCondition.notify_all(); } } void EventThread::onScreenAcquired() { std::lock_guard<std::mutex> lock(mMutex); - if (mUseSoftwareVSync) { - // resume use of h/w vsync - mUseSoftwareVSync = false; + if (mVSyncState.synthetic) { + mVSyncState.synthetic = false; mCondition.notify_all(); } } void EventThread::onVSyncEvent(nsecs_t timestamp) { std::lock_guard<std::mutex> lock(mMutex); - mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[0].header.id = 0; - mVSyncEvent[0].header.timestamp = timestamp; - mVSyncEvent[0].vsync.count++; + + mPendingEvents.push_back(makeVSync(mVSyncState.displayId, timestamp, ++mVSyncState.count)); mCondition.notify_all(); } void EventThread::onHotplugReceived(DisplayType displayType, bool connected) { std::lock_guard<std::mutex> lock(mMutex); - DisplayEventReceiver::Event event; - event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; - event.header.id = displayType == DisplayType::Primary ? 0 : 1; - event.header.timestamp = systemTime(); - event.hotplug.connected = connected; - - mPendingEvents.push(event); + const uint32_t displayId = displayType == DisplayType::Primary ? 0 : 1; + mPendingEvents.push_back(makeHotplug(displayId, systemTime(), connected)); mCondition.notify_all(); } -void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { - std::unique_lock<std::mutex> lock(mMutex); +void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { + DisplayEventConsumers consumers; + while (mKeepRunning) { - DisplayEventReceiver::Event event; - std::vector<sp<EventThreadConnection>> signalConnections; - signalConnections = waitForEventLocked(&lock, &event); - - // dispatch events to listeners... - for (const sp<EventThreadConnection>& conn : signalConnections) { - // now see if we still need to report this event - status_t err = conn->postEvent(event); - if (err == -EAGAIN || err == -EWOULDBLOCK) { - // The destination doesn't accept events anymore, it's probably - // full. For now, we just drop the events on the floor. - // FIXME: Note that some events cannot be dropped and would have - // to be re-sent later. - // Right-now we don't have the ability to do this. - ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type, - conn.get()); - } else if (err < 0) { - // handle any other error on the pipe as fatal. the only - // reasonable thing to do is to clean-up this connection. - // The most common error we'll get here is -EPIPE. - removeDisplayEventConnectionLocked(conn); - } - } - } -} + std::optional<DisplayEventReceiver::Event> event; -// This will return when (1) a vsync event has been received, and (2) there was -// at least one connection interested in receiving it when we started waiting. -std::vector<sp<EventThreadConnection>> EventThread::waitForEventLocked( - std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* outEvent) { - std::vector<sp<EventThreadConnection>> signalConnections; - - while (signalConnections.empty() && mKeepRunning) { - bool eventPending = false; - bool waitForVSync = false; - - size_t vsyncCount = 0; - nsecs_t timestamp = 0; - for (auto& event : mVSyncEvent) { - timestamp = event.header.timestamp; - if (timestamp) { - // we have a vsync event to dispatch - if (mInterceptVSyncsCallback) { - mInterceptVSyncsCallback(timestamp); - } - *outEvent = event; - event.header.timestamp = 0; - vsyncCount = event.vsync.count; - break; - } + // Determine next event to dispatch. + if (!mPendingEvents.empty()) { + event = mPendingEvents.front(); + mPendingEvents.pop_front(); } - if (!timestamp) { - // no vsync event, see if there are some other event - eventPending = !mPendingEvents.empty(); - if (eventPending) { - // we have some other event to dispatch - *outEvent = mPendingEvents.front(); - mPendingEvents.pop(); - } + const bool vsyncPending = + event && event->header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + + if (mInterceptVSyncsCallback && vsyncPending) { + mInterceptVSyncsCallback(event->header.timestamp); } - // find out connections waiting for events + bool vsyncRequested = false; + + // Find connections that should consume this event. auto it = mDisplayEventConnections.begin(); while (it != mDisplayEventConnections.end()) { - sp<EventThreadConnection> connection(it->promote()); - if (connection != nullptr) { - bool added = false; - if (connection->count >= 0) { - // we need vsync events because at least - // one connection is waiting for it - waitForVSync = true; - if (timestamp) { - // we consume the event only if it's time - // (ie: we received a vsync event) - if (connection->count == 0) { - // fired this time around - connection->count = -1; - signalConnections.push_back(connection); - added = true; - } else if (connection->count == 1 || - (vsyncCount % connection->count) == 0) { - // continuous event, and time to report it - signalConnections.push_back(connection); - added = true; - } - } - } + if (const auto connection = it->promote()) { + vsyncRequested |= connection->vsyncRequest != VSyncRequest::None; - if (eventPending && !timestamp && !added) { - // we don't have a vsync event to process - // (timestamp==0), but we have some pending - // messages. - signalConnections.push_back(connection); + if (event && shouldConsumeEvent(*event, connection)) { + consumers.push_back(connection); } + ++it; } else { - // we couldn't promote this reference, the connection has - // died, so clean-up! it = mDisplayEventConnections.erase(it); } } + if (!consumers.empty()) { + dispatchEvent(*event, consumers); + consumers.clear(); + } + // Here we figure out if we need to enable or disable vsyncs - if (timestamp && !waitForVSync) { + if (vsyncPending && !vsyncRequested) { // we received a VSYNC but we have no clients // don't report it, and disable VSYNC events disableVSyncLocked(); - } else if (!timestamp && waitForVSync) { + } else if (!vsyncPending && vsyncRequested) { // we have at least one client, so we want vsync enabled // (TODO: this function is called right after we finish // notifying clients of a vsync, so this call will be made @@ -368,53 +347,74 @@ std::vector<sp<EventThreadConnection>> EventThread::waitForEventLocked( enableVSyncLocked(); } - // note: !timestamp implies signalConnections.isEmpty(), because we - // don't populate signalConnections if there's no vsync pending - if (!timestamp && !eventPending) { - // wait for something to happen - if (waitForVSync) { - // This is where we spend most of our time, waiting - // for vsync events and new client registrations. - // - // If the screen is off, we can't use h/w vsync, so we - // use a 16ms timeout instead. It doesn't need to be - // precise, we just need to keep feeding our clients. - // - // We don't want to stall if there's a driver bug, so we - // use a (long) timeout when waiting for h/w vsync, and - // generate fake events when necessary. - bool softwareSync = mUseSoftwareVSync; - auto timeout = softwareSync ? 16ms : 1000ms; - if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) { - if (!softwareSync) { - ALOGW("Timed out waiting for hw vsync; faking it"); - } - // FIXME: how do we decide which display id the fake - // vsync came from ? - mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[0].header.id = 0; - mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); - mVSyncEvent[0].vsync.count++; - } - } else { - // Nobody is interested in vsync, so we just want to sleep. - // h/w vsync should be disabled, so this will wait until we - // get a new connection, or an existing connection becomes - // interested in receiving vsync again. - mCondition.wait(*lock); + if (event) { + continue; + } + + // Wait for event or client registration/request. + if (vsyncRequested) { + // Generate a fake VSYNC after a long timeout in case the driver stalls. When the + // display is off, keep feeding clients at 60 Hz. + const auto timeout = mVSyncState.synthetic ? 16ms : 1000ms; + if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) { + ALOGW_IF(!mVSyncState.synthetic, "Faking VSYNC due to driver stall"); + + mPendingEvents.push_back(makeVSync(mVSyncState.displayId, + systemTime(SYSTEM_TIME_MONOTONIC), + ++mVSyncState.count)); } + } else { + mCondition.wait(lock); } } +} + +bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, + const sp<EventThreadConnection>& connection) const { + switch (event.header.type) { + case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: + return true; + + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + switch (connection->vsyncRequest) { + case VSyncRequest::None: + return false; + case VSyncRequest::Single: + connection->vsyncRequest = VSyncRequest::None; + return true; + case VSyncRequest::Periodic: + return true; + default: + return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0; + } - // here we're guaranteed to have a timestamp and some connections to signal - // (The connections might have dropped out of mDisplayEventConnections - // while we were asleep, but we'll still have strong references to them.) - return signalConnections; + default: + return false; + } +} + +void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, + const DisplayEventConsumers& consumers) { + for (const auto& consumer : consumers) { + switch (consumer->postEvent(event)) { + case NO_ERROR: + break; + + case -EAGAIN: + // TODO: Try again if pipe is full. + ALOGW("Failed dispatching %s for %s", toString(event).c_str(), + toString(*consumer).c_str()); + break; + + default: + // Treat EPIPE and other errors as fatal. + removeDisplayEventConnectionLocked(consumer); + } + } } void EventThread::enableVSyncLocked() { - if (!mUseSoftwareVSync) { - // never enable h/w VSYNC when screen is off + if (!mVSyncState.synthetic) { if (!mVsyncEnabled) { mVsyncEnabled = true; mVSyncSource->setCallback(this); @@ -434,16 +434,22 @@ void EventThread::disableVSyncLocked() { void EventThread::dump(std::string& result) const { std::lock_guard<std::mutex> lock(mMutex); - StringAppendF(&result, "VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled"); - StringAppendF(&result, " soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled"); - StringAppendF(&result, " numListeners=%zu,\n events-delivered: %u\n", - mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count); - for (const wp<EventThreadConnection>& weak : mDisplayEventConnections) { - sp<EventThreadConnection> connection = weak.promote(); - StringAppendF(&result, " %p: count=%d\n", connection.get(), - connection != nullptr ? connection->count : 0); + + StringAppendF(&result, "%s: VSYNC %s\n", mThreadName, mDebugVsyncEnabled ? "on" : "off"); + StringAppendF(&result, " VSyncState{displayId=%u, count=%u%s}\n", mVSyncState.displayId, + mVSyncState.count, mVSyncState.synthetic ? ", synthetic" : ""); + + StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size()); + for (const auto& event : mPendingEvents) { + StringAppendF(&result, " %s\n", toString(event).c_str()); + } + + StringAppendF(&result, " connections (count=%zu):\n", mDisplayEventConnections.size()); + for (const auto& ptr : mDisplayEventConnections) { + if (const auto connection = ptr.promote()) { + StringAppendF(&result, " %s\n", toString(*connection).c_str()); + } } - StringAppendF(&result, " other-events-pending: %zu\n", mPendingEvents.size()); } } // namespace impl diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index a41188569c..1a8ebb7903 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -18,11 +18,10 @@ #include <sys/types.h> -#include <array> #include <condition_variable> #include <cstdint> +#include <deque> #include <mutex> -#include <queue> #include <thread> #include <vector> @@ -46,6 +45,13 @@ class SurfaceFlinger; using ResyncCallback = std::function<void()>; +enum class VSyncRequest { + None = -1, + Single = 0, + Periodic = 1, + // Subsequent values are periods. +}; + class VSyncSource { public: class Callback { @@ -68,7 +74,7 @@ public: virtual status_t postEvent(const DisplayEventReceiver::Event& event); status_t stealReceiveChannel(gui::BitTube* outChannel) override; - status_t setVsyncRate(uint32_t count) override; + status_t setVsyncRate(uint32_t rate) override; void requestNextVsync() override; // asynchronous // Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh // in order to update the configs. @@ -77,10 +83,7 @@ public: // Called in response to requestNextVsync. const ResyncCallback resyncCallback; - // count >= 1 : continuous event. count is the vsync rate - // count == 0 : one-shot event that has not fired - // count ==-1 : one-shot event that fired this round / disabled - int32_t count; + VSyncRequest vsyncRequest = VSyncRequest::None; private: virtual void onFirstRef(); @@ -113,7 +116,7 @@ public: virtual status_t registerDisplayEventConnection( const sp<EventThreadConnection>& connection) = 0; - virtual void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) = 0; + virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0; // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer. virtual void requestNextVsync(const sp<EventThreadConnection>& connection, bool resetIdleTimer) = 0; @@ -137,7 +140,7 @@ public: sp<EventThreadConnection> createEventConnection(ResyncCallback resyncCallback) const override; status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override; - void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) override; + void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override; void requestNextVsync(const sp<EventThreadConnection>& connection, bool resetIdleTimer) override; @@ -157,18 +160,22 @@ public: private: friend EventThreadTest; + using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>; + // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete. EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc, InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); + void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex); - void threadMain(); - std::vector<sp<EventThreadConnection>> waitForEventLocked(std::unique_lock<std::mutex>* lock, - DisplayEventReceiver::Event* event) - REQUIRES(mMutex); + bool shouldConsumeEvent(const DisplayEventReceiver::Event& event, + const sp<EventThreadConnection>& connection) const REQUIRES(mMutex); + void dispatchEvent(const DisplayEventReceiver::Event& event, + const DisplayEventConsumers& consumers) REQUIRES(mMutex); void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection) REQUIRES(mMutex); + void enableVSyncLocked() REQUIRES(mMutex); void disableVSyncLocked() REQUIRES(mMutex); @@ -181,18 +188,30 @@ private: // TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete. VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr; std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr; - // constants + const InterceptVSyncsCallback mInterceptVSyncsCallback; + const char* const mThreadName; std::thread mThread; mutable std::mutex mMutex; mutable std::condition_variable mCondition; - // protected by mLock std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex); - std::queue<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex); - std::array<DisplayEventReceiver::Event, 2> mVSyncEvent GUARDED_BY(mMutex); - bool mUseSoftwareVSync GUARDED_BY(mMutex) = false; + std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex); + + // VSYNC state of connected display. + struct VSyncState { + uint32_t displayId = 0; + + // Number of VSYNC events since display was connected. + uint32_t count = 0; + + // True if VSYNC should be faked, e.g. when display is off. + bool synthetic = false; + }; + + VSyncState mVSyncState GUARDED_BY(mMutex); + bool mVsyncEnabled GUARDED_BY(mMutex) = false; bool mKeepRunning GUARDED_BY(mMutex) = true; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 8746a6805c..0fce71a0d7 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -28,6 +28,7 @@ #include <functional> #include <mutex> #include <optional> +#include <unordered_map> #include <cutils/properties.h> #include <log/log.h> @@ -4363,8 +4364,8 @@ void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { // --------------------------------------------------------------------------- -status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto) - NO_THREAD_SAFETY_ANALYSIS { +status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, + bool asProto) NO_THREAD_SAFETY_ANALYSIS { std::string result; IPCThreadState* ipc = IPCThreadState::self(); @@ -4388,114 +4389,36 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro strerror(-err), err); } - bool dumpAll = true; - size_t index = 0; - size_t numArgs = args.size(); - - if (numArgs) { - if ((index < numArgs) && - (args[index] == String16("--list"))) { - index++; - listLayersLocked(args, index, result); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--latency"))) { - index++; - dumpStatsLocked(args, index, result); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--latency-clear"))) { - index++; - clearStatsLocked(args, index, result); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--dispsync"))) { - index++; - mPrimaryDispSync->dump(result); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--static-screen"))) { - index++; - dumpStaticScreenStats(result); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--frame-events"))) { - index++; - dumpFrameEventsLocked(result); - dumpAll = false; - } - - if ((index < numArgs) && (args[index] == String16("--wide-color"))) { - index++; - dumpWideColorInfo(result); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--enable-layer-stats"))) { - index++; - mLayerStats.enable(); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--disable-layer-stats"))) { - index++; - mLayerStats.disable(); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--clear-layer-stats"))) { - index++; - mLayerStats.clear(); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--dump-layer-stats"))) { - index++; - mLayerStats.dump(result); - dumpAll = false; - } - - if ((index < numArgs) && - (args[index] == String16("--frame-composition"))) { - index++; - dumpFrameCompositionInfo(result); - dumpAll = false; - } + using namespace std::string_literals; + + static const std::unordered_map<std::string, Dumper> dumpers = { + {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })}, + {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })}, + {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)}, + {"--dispsync"s, dumper([this](std::string& s) { mPrimaryDispSync->dump(s); })}, + {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })}, + {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })}, + {"--frame-composition"s, dumper(&SurfaceFlinger::dumpFrameCompositionInfo)}, + {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)}, + {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, + {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, + {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)}, + {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)}, + {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)}, + {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)}, + {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)}, + }; - if ((index < numArgs) && - (args[index] == String16("--display-identification"))) { - index++; - dumpDisplayIdentificationData(result); - dumpAll = false; - } + const auto flag = args.empty() ? ""s : std::string(String8(args[0])); - if ((index < numArgs) && (args[index] == String16("--timestats"))) { - index++; - mTimeStats->parseArgs(asProto, args, index, result); - dumpAll = false; - } - } - - if (dumpAll) { + if (const auto it = dumpers.find(flag); it != dumpers.end()) { + (it->second)(args, asProto, result); + } else { if (asProto) { LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current); result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize()); } else { - dumpAllLocked(args, index, result); + dumpAllLocked(args, result); } } @@ -4507,43 +4430,29 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro return NO_ERROR; } -void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */, size_t& /* index */, - std::string& result) const { +void SurfaceFlinger::listLayersLocked(std::string& result) const { mCurrentState.traverseInZOrder( [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); }); } -void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index, - std::string& result) const { - String8 name; - if (index < args.size()) { - name = String8(args[index]); - index++; - } - +void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const { StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod()); - if (name.isEmpty()) { - mAnimFrameTracker.dumpStats(result); - } else { + if (args.size() > 1) { + const auto name = String8(args[1]); mCurrentState.traverseInZOrder([&](Layer* layer) { if (name == layer->getName()) { layer->dumpFrameStats(result); } }); + } else { + mAnimFrameTracker.dumpStats(result); } } -void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index, - std::string& /* result */) { - String8 name; - if (index < args.size()) { - name = String8(args[index]); - index++; - } - +void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { mCurrentState.traverseInZOrder([&](Layer* layer) { - if (name.isEmpty() || (name == layer->getName())) { + if (args.size() < 2 || String8(args[1]) == layer->getName()) { layer->clearFrameStats(); } }); @@ -4551,6 +4460,10 @@ void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& inde mAnimFrameTracker.clearStats(); } +void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const { + mTimeStats->parseArgs(asProto, args, result); +} + // This should only be called from the main thread. Otherwise it would need // the lock and should use mCurrentState rather than mDrawingState. void SurfaceFlinger::logFrameStats() { @@ -4576,6 +4489,26 @@ void SurfaceFlinger::appendSfConfigString(std::string& result) const { result.append("]"); } +void SurfaceFlinger::dumpVSync(std::string& result) const { + const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets(); + const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets(); + StringAppendF(&result, + " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" + " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" + "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n" + " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", + vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset, sfEarlyOffset, + appEarlyGlOffset, sfEarlyGlOffset, dispSyncPresentTimeOffset, getVsyncPeriod()); + + StringAppendF(&result, "Scheduler: %s\n\n", mUseScheduler ? "enabled" : "disabled"); + + if (mUseScheduler) { + mScheduler->dump(mAppConnectionHandle, result); + } else { + mEventThread->dump(result); + } +} + void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const { result.append("Static screen stats:\n"); for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) { @@ -4779,15 +4712,8 @@ LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& disp return layersProto; } -void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, - std::string& result) const { - bool colorize = false; - if (index < args.size() - && (args[index] == String16("--color"))) { - colorize = true; - index++; - } - +void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const { + const bool colorize = !args.empty() && args[0] == String16("--color"); Colorizer colorizer(colorize); // figure out if we're stuck somewhere @@ -4817,27 +4743,14 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.append("Sync configuration: "); colorizer.reset(result); result.append(SyncFeatures::getInstance().toString()); - result.append("\n"); + result.append("\n\n"); colorizer.bold(result); - result.append("DispSync configuration:\n"); + result.append("VSYNC configuration:\n"); colorizer.reset(result); - - const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets(); - const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets(); - StringAppendF(&result, - "app phase %" PRId64 " ns, " - "sf phase %" PRId64 " ns, " - "early app phase %" PRId64 " ns, " - "early sf phase %" PRId64 " ns, " - "early app gl phase %" PRId64 " ns, " - "early sf gl phase %" PRId64 " ns, " - "present offset %" PRId64 " ns (refresh %" PRId64 " ns)\n", - vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset, sfEarlyOffset, - appEarlyGlOffset, sfEarlyGlOffset, dispSyncPresentTimeOffset, getVsyncPeriod()); - - // Dump static screen stats + dumpVSync(result); result.append("\n"); + dumpStaticScreenStats(result); result.append("\n"); @@ -4911,17 +4824,6 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0); - StringAppendF(&result, " use Scheduler: %s\n", mUseScheduler ? "true" : "false"); - /* - * VSYNC state - */ - if (mUseScheduler) { - mScheduler->dump(mAppConnectionHandle, result); - } else { - mEventThread->dump(result); - } - result.append("\n"); - /* * Tracing state */ diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 28ed00452b..68a602cf61 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -69,6 +69,7 @@ #include <atomic> #include <cstdint> +#include <functional> #include <map> #include <memory> #include <mutex> @@ -76,6 +77,7 @@ #include <set> #include <string> #include <thread> +#include <type_traits> #include <unordered_map> #include <utility> @@ -787,19 +789,9 @@ private: }; } - /* ------------------------------------------------------------------------ - * Debugging & dumpsys + /* + * Display identification */ -public: - status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) { - return doDump(fd, Vector<String16>(), asProto); - } - - status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) { - return doDump(fd, args, asProto); - } - -private: sp<IBinder> getPhysicalDisplayToken(DisplayId displayId) const { const auto it = mPhysicalDisplayTokens.find(displayId); return it != mPhysicalDisplayTokens.end() ? it->second : nullptr; @@ -831,17 +823,48 @@ private: return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt; } - void listLayersLocked(const Vector<String16>& args, size_t& index, std::string& result) const; - void dumpStatsLocked(const Vector<String16>& args, size_t& index, std::string& result) const - REQUIRES(mStateLock); - void clearStatsLocked(const Vector<String16>& args, size_t& index, std::string& result); - void dumpAllLocked(const Vector<String16>& args, size_t& index, std::string& result) const - REQUIRES(mStateLock); + /* + * Debugging & dumpsys + */ bool startDdmConnection(); - void appendSfConfigString(std::string& result) const; + using DumpArgs = Vector<String16>; + using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>; + + template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr> + static Dumper dumper(F&& dump) { + using namespace std::placeholders; + return std::bind(std::forward<F>(dump), _3); + } + + template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> + Dumper dumper(F dump) { + using namespace std::placeholders; + return std::bind(dump, this, _3); + } + + template <typename F> + Dumper argsDumper(F dump) { + using namespace std::placeholders; + return std::bind(dump, this, _1, _3); + } + + template <typename F> + Dumper protoDumper(F dump) { + using namespace std::placeholders; + return std::bind(dump, this, _1, _2, _3); + } + + void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); + + void appendSfConfigString(std::string& result) const; + void listLayersLocked(std::string& result) const; + void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); + void clearStatsLocked(const DumpArgs& args, std::string& result); + void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const; void logFrameStats(); + void dumpVSync(std::string& result) const REQUIRES(mStateLock); void dumpStaticScreenStats(std::string& result) const; // Not const because each Layer needs to query Fences and cache timestamps. void dumpFrameEventsLocked(std::string& result); @@ -858,7 +881,16 @@ private: bool isLayerTripleBufferingDisabled() const { return this->mLayerTripleBufferingDisabled; } - status_t doDump(int fd, const Vector<String16>& args, bool asProto); + + status_t doDump(int fd, const DumpArgs& args, bool asProto); + + status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override { + return doDump(fd, DumpArgs(), asProto); + } + + status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override { + return doDump(fd, args, asProto); + } /* ------------------------------------------------------------------------ * VrFlinger diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 6a5488aa2c..8d3776bd4e 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -32,19 +32,12 @@ namespace android { -void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index, - std::string& result) { +void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) { ATRACE_CALL(); - if (args.size() > index + 10) { - ALOGD("Invalid args count"); - return; - } - std::unordered_map<std::string, int32_t> argsMap; - while (index < args.size()) { + for (size_t index = 0; index < args.size(); ++index) { argsMap[std::string(String8(args[index]).c_str())] = index; - ++index; } if (argsMap.count("-disable")) { diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 71c3ed7e8e..e8fbcab9c0 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -77,7 +77,7 @@ public: TimeStats() = default; ~TimeStats() = default; - void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, std::string& result); + void parseArgs(bool asProto, const Vector<String16>& args, std::string& result); bool isEnabled(); void incrementTotalFrames(); diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 86f1a39b81..0f95cf910b 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -131,7 +131,6 @@ public: }; std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) { - size_t index = 0; std::string result; Vector<String16> args; @@ -162,7 +161,7 @@ std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) { ALOGD("Invalid control command"); } - EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, index, result)); + EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, result)); return result; } |