Loading services/surfaceflinger/SurfaceFlinger.cpp +11 −10 Original line number Diff line number Diff line Loading @@ -6244,9 +6244,9 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--frontend"s, mainThreadDumper(&SurfaceFlinger::dumpFrontEnd)}, {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)}, {"--hwclayers"s, mainThreadDumper(&SurfaceFlinger::dumpHwcLayersMinidump)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)}, {"--latency"s, argsMainThreadDumper(&SurfaceFlinger::dumpStats)}, {"--latency-clear"s, argsMainThreadDumper(&SurfaceFlinger::clearStats)}, {"--list"s, mainThreadDumper(&SurfaceFlinger::listLayers)}, {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)}, {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)}, {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)}, Loading Loading @@ -6279,28 +6279,29 @@ status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { return doDump(fd, DumpArgs(), asProto); } void SurfaceFlinger::listLayersLocked(std::string& result) const { mCurrentState.traverseInZOrder( [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getDebugName()); }); void SurfaceFlinger::listLayers(std::string& result) const { for (const auto& layer : mLayerLifecycleManager.getLayers()) { StringAppendF(&result, "%s\n", layer->getDebugString().c_str()); } } void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const { void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const { StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC()); if (args.size() < 2) return; const auto name = String8(args[1]); mCurrentState.traverseInZOrder([&](Layer* layer) { traverseLegacyLayers([&](Layer* layer) { if (layer->getName() == name.c_str()) { layer->dumpFrameStats(result); } }); } void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { void SurfaceFlinger::clearStats(const DumpArgs& args, std::string&) { const bool clearAll = args.size() < 2; const auto name = clearAll ? String8() : String8(args[1]); mCurrentState.traverse([&](Layer* layer) { traverseLegacyLayers([&](Layer* layer) { if (clearAll || layer->getName() == name.c_str()) { layer->clearFrameStats(); } Loading services/surfaceflinger/SurfaceFlinger.h +16 −7 Original line number Diff line number Diff line Loading @@ -509,10 +509,7 @@ private: return lockedDumper(std::bind(dump, this, _1, _2, _3)); } template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> Dumper mainThreadDumper(F dump) { using namespace std::placeholders; Dumper dumper = std::bind(dump, this, _3); Dumper mainThreadDumperImpl(Dumper dumper) { return [this, dumper](const DumpArgs& args, bool asProto, std::string& result) -> void { mScheduler ->schedule( Loading @@ -522,6 +519,18 @@ private: }; } template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> Dumper mainThreadDumper(F dump) { using namespace std::placeholders; return mainThreadDumperImpl(std::bind(dump, this, _3)); } template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> Dumper argsMainThreadDumper(F dump) { using namespace std::placeholders; return mainThreadDumperImpl(std::bind(dump, this, _1, _3)); } // Maximum allowed number of display frames that can be set through backdoor static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048; Loading Loading @@ -1113,9 +1122,9 @@ private: void dumpHwcLayersMinidumpLockedLegacy(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 listLayers(std::string& result) const; void dumpStats(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); void clearStats(const DumpArgs& args, std::string& result); void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const; void dumpFrameTimeline(const DumpArgs& args, std::string& result) const; void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext); Loading services/surfaceflinger/tests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ cc_test { "DereferenceSurfaceControl_test.cpp", "DisplayConfigs_test.cpp", "DisplayEventReceiver_test.cpp", "Dumpsys_test.cpp", "EffectLayer_test.cpp", "HdrSdrRatioOverlay_test.cpp", "InvalidHandles_test.cpp", Loading services/surfaceflinger/tests/Dumpsys_test.cpp 0 → 100644 +109 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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. */ #include <android/native_window.h> #include <gtest/gtest.h> #include <gui/SurfaceComposerClient.h> #include "android-base/stringprintf.h" #include "utils/Errors.h" namespace android { namespace { status_t runShellCommand(const std::string& cmd, std::string& result) { FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) { return UNKNOWN_ERROR; } char buffer[1024]; while (fgets(buffer, sizeof(buffer), pipe) != NULL) { result += buffer; } pclose(pipe); return OK; } } // namespace using android::hardware::graphics::common::V1_1::BufferUsage; class WaitForCompletedCallback { public: WaitForCompletedCallback() = default; ~WaitForCompletedCallback() = default; static void transactionCompletedCallback(void* callbackContext, nsecs_t /* latchTime */, const sp<Fence>& /* presentFence */, const std::vector<SurfaceControlStats>& /* stats */) { ASSERT_NE(callbackContext, nullptr) << "failed to get callback context"; WaitForCompletedCallback* context = static_cast<WaitForCompletedCallback*>(callbackContext); context->notify(); } void wait() { std::unique_lock lock(mMutex); cv.wait(lock, [this] { return mCallbackReceived; }); } void notify() { std::unique_lock lock(mMutex); mCallbackReceived = true; cv.notify_one(); } private: std::mutex mMutex; std::condition_variable cv; bool mCallbackReceived = false; }; TEST(Dumpsys, listLayers) { sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, client->initCheck()); auto newLayer = client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0); std::string layersAsString; EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --list", layersAsString)); EXPECT_NE(strstr(layersAsString.c_str(), ""), nullptr); } TEST(Dumpsys, stats) { sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, client->initCheck()); auto newLayer = client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0); uint64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, 1u, usageFlags, "test"); WaitForCompletedCallback callback; SurfaceComposerClient::Transaction() .setBuffer(newLayer, buffer) .addTransactionCompletedCallback(WaitForCompletedCallback::transactionCompletedCallback, &callback) .apply(); callback.wait(); std::string stats; std::string layerName = base::StringPrintf("MY_TEST_LAYER#%d", newLayer->getLayerId()); EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency " + layerName, stats)); EXPECT_NE(std::string(""), stats); EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency-clear " + layerName, stats)); } } // namespace android Loading
services/surfaceflinger/SurfaceFlinger.cpp +11 −10 Original line number Diff line number Diff line Loading @@ -6244,9 +6244,9 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--frontend"s, mainThreadDumper(&SurfaceFlinger::dumpFrontEnd)}, {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)}, {"--hwclayers"s, mainThreadDumper(&SurfaceFlinger::dumpHwcLayersMinidump)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)}, {"--latency"s, argsMainThreadDumper(&SurfaceFlinger::dumpStats)}, {"--latency-clear"s, argsMainThreadDumper(&SurfaceFlinger::clearStats)}, {"--list"s, mainThreadDumper(&SurfaceFlinger::listLayers)}, {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)}, {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)}, {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)}, Loading Loading @@ -6279,28 +6279,29 @@ status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { return doDump(fd, DumpArgs(), asProto); } void SurfaceFlinger::listLayersLocked(std::string& result) const { mCurrentState.traverseInZOrder( [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getDebugName()); }); void SurfaceFlinger::listLayers(std::string& result) const { for (const auto& layer : mLayerLifecycleManager.getLayers()) { StringAppendF(&result, "%s\n", layer->getDebugString().c_str()); } } void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const { void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const { StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC()); if (args.size() < 2) return; const auto name = String8(args[1]); mCurrentState.traverseInZOrder([&](Layer* layer) { traverseLegacyLayers([&](Layer* layer) { if (layer->getName() == name.c_str()) { layer->dumpFrameStats(result); } }); } void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { void SurfaceFlinger::clearStats(const DumpArgs& args, std::string&) { const bool clearAll = args.size() < 2; const auto name = clearAll ? String8() : String8(args[1]); mCurrentState.traverse([&](Layer* layer) { traverseLegacyLayers([&](Layer* layer) { if (clearAll || layer->getName() == name.c_str()) { layer->clearFrameStats(); } Loading
services/surfaceflinger/SurfaceFlinger.h +16 −7 Original line number Diff line number Diff line Loading @@ -509,10 +509,7 @@ private: return lockedDumper(std::bind(dump, this, _1, _2, _3)); } template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> Dumper mainThreadDumper(F dump) { using namespace std::placeholders; Dumper dumper = std::bind(dump, this, _3); Dumper mainThreadDumperImpl(Dumper dumper) { return [this, dumper](const DumpArgs& args, bool asProto, std::string& result) -> void { mScheduler ->schedule( Loading @@ -522,6 +519,18 @@ private: }; } template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> Dumper mainThreadDumper(F dump) { using namespace std::placeholders; return mainThreadDumperImpl(std::bind(dump, this, _3)); } template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> Dumper argsMainThreadDumper(F dump) { using namespace std::placeholders; return mainThreadDumperImpl(std::bind(dump, this, _1, _3)); } // Maximum allowed number of display frames that can be set through backdoor static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048; Loading Loading @@ -1113,9 +1122,9 @@ private: void dumpHwcLayersMinidumpLockedLegacy(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 listLayers(std::string& result) const; void dumpStats(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); void clearStats(const DumpArgs& args, std::string& result); void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const; void dumpFrameTimeline(const DumpArgs& args, std::string& result) const; void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext); Loading
services/surfaceflinger/tests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ cc_test { "DereferenceSurfaceControl_test.cpp", "DisplayConfigs_test.cpp", "DisplayEventReceiver_test.cpp", "Dumpsys_test.cpp", "EffectLayer_test.cpp", "HdrSdrRatioOverlay_test.cpp", "InvalidHandles_test.cpp", Loading
services/surfaceflinger/tests/Dumpsys_test.cpp 0 → 100644 +109 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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. */ #include <android/native_window.h> #include <gtest/gtest.h> #include <gui/SurfaceComposerClient.h> #include "android-base/stringprintf.h" #include "utils/Errors.h" namespace android { namespace { status_t runShellCommand(const std::string& cmd, std::string& result) { FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) { return UNKNOWN_ERROR; } char buffer[1024]; while (fgets(buffer, sizeof(buffer), pipe) != NULL) { result += buffer; } pclose(pipe); return OK; } } // namespace using android::hardware::graphics::common::V1_1::BufferUsage; class WaitForCompletedCallback { public: WaitForCompletedCallback() = default; ~WaitForCompletedCallback() = default; static void transactionCompletedCallback(void* callbackContext, nsecs_t /* latchTime */, const sp<Fence>& /* presentFence */, const std::vector<SurfaceControlStats>& /* stats */) { ASSERT_NE(callbackContext, nullptr) << "failed to get callback context"; WaitForCompletedCallback* context = static_cast<WaitForCompletedCallback*>(callbackContext); context->notify(); } void wait() { std::unique_lock lock(mMutex); cv.wait(lock, [this] { return mCallbackReceived; }); } void notify() { std::unique_lock lock(mMutex); mCallbackReceived = true; cv.notify_one(); } private: std::mutex mMutex; std::condition_variable cv; bool mCallbackReceived = false; }; TEST(Dumpsys, listLayers) { sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, client->initCheck()); auto newLayer = client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0); std::string layersAsString; EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --list", layersAsString)); EXPECT_NE(strstr(layersAsString.c_str(), ""), nullptr); } TEST(Dumpsys, stats) { sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, client->initCheck()); auto newLayer = client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0); uint64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, 1u, usageFlags, "test"); WaitForCompletedCallback callback; SurfaceComposerClient::Transaction() .setBuffer(newLayer, buffer) .addTransactionCompletedCallback(WaitForCompletedCallback::transactionCompletedCallback, &callback) .apply(); callback.wait(); std::string stats; std::string layerName = base::StringPrintf("MY_TEST_LAYER#%d", newLayer->getLayerId()); EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency " + layerName, stats)); EXPECT_NE(std::string(""), stats); EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency-clear " + layerName, stats)); } } // namespace android