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

Commit d1a0578e authored by Vishnu Nair's avatar Vishnu Nair
Browse files

Add support for --list debug command and fix --latency

Fixes: 329247258
Test: presubmit
Change-Id: I680207e5ed7e4c7cd9fba9b502b3caeffa415c55
parent acb53f92
Loading
Loading
Loading
Loading
+11 −10
Original line number Diff line number Diff line
@@ -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)},
@@ -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();
        }
+16 −7
Original line number Diff line number Diff line
@@ -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(
@@ -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;

@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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",
+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