/* Copyright Statement: * * This software/firmware and related documentation ("MediaTek Software") are * protected under relevant copyright laws. The information contained herein is * confidential and proprietary to MediaTek Inc. and/or its licensors. Without * the prior written permission of MediaTek inc. and/or its licensors, any * reproduction, modification, use or disclosure of MediaTek Software, and * information contained herein, in whole or in part, shall be strictly * prohibited. * * MediaTek Inc. (C) 2022. All rights reserved. * * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. * * The following software/firmware and/or related documentation ("MediaTek * Software") have been modified by MediaTek Inc. All revisions are subject to * any receiver's applicable license agreements with MediaTek Inc. */ #pragma once #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include #include #include #include #include typedef int (*MBNotifyT4)(char* layerName, long long frameId, long long vsyncSfTime, long long t4, long long sfDuration, long long vsyncPeriod, int abnormal, long long pendingBuffer, int decoupleMode); typedef int (*MBGetTouchDebugEnabled)(int &enabled); namespace android { // --------------------------------------------------------------------------- class FenceTracer { public: static bool isSupportMBrain() { static bool enable = false; static bool read = false; if (!read) { char platform[PROPERTY_VALUE_MAX] = {0}; property_get("ro.board.platform", platform, ""); if (strcmp(platform, "mt6985") == 0 || strcmp(platform, "mt6897") == 0 || strcmp(platform, "mt6989") == 0 || strcmp(platform, "mt6878")) { enable = android::base::GetBoolProperty("debug.sf.mbrain_support", false); } else { enable = false; } read = true; } return enable; } private: FenceTracer() { } class FenceMonitor { public: explicit FenceMonitor(const char* name) : mName(name) { if (isSupportMBrain()) { mMBrainHandle = dlopen("libmbrainSDK.so", RTLD_LAZY); if (mMBrainHandle != nullptr) { char *error = nullptr; dlerror(); mMBnotifyT4Func = reinterpret_cast(dlsym(mMBrainHandle, "NotifyT4InfoHook")); if ((error = dlerror()) != nullptr) { ALOGW("%s: dlsym(NotifyT4InfoHook) failed, err = %s", __func__, error); } else { ALOGI("%s: dlsym(NotifyT4InfoHook) success", __func__); } mMBGetTouchDebugEnabledFunc = reinterpret_cast(dlsym(mMBrainHandle, "GetTouchDebugEnabledHook")); if ((error = dlerror()) != nullptr) { ALOGW("%s: dlsym(GetTouchDebugEnabledHook) failed, err = %s", __func__, error); } else { ALOGI("%s: dlsym(GetTouchDebugEnabledHook) success", __func__); } } else { ALOGW("%s: load MBrain so failed", __func__); } } std::thread thread(&FenceMonitor::loop, this); pthread_setname_np(thread.native_handle(), mName); thread.detach(); } ~FenceMonitor() { if (isSupportMBrain()) { if (mMBrainHandle != nullptr) { dlclose(mMBrainHandle); } } } void queueFrameNumber(const std::string& layerName, const uint64_t frameNumber, const nsecs_t vsyncSfTime, const int32_t pendingBufferCount, const bool bDecoupleMode, const nsecs_t sfDuration, const nsecs_t vsyncPeriod) { std::lock_guard lock(mMutex); auto it = mFrameByVsyncSf.find(vsyncSfTime); if (it != mFrameByVsyncSf.end()) { auto& vecLayerFrameNum = it->second; vecLayerFrameNum.push_back(std::make_tuple(layerName, frameNumber, pendingBufferCount, bDecoupleMode, sfDuration, vsyncPeriod)); } else { std::vector> vecLayerFrameNum; vecLayerFrameNum.push_back(std::make_tuple(layerName, frameNumber, pendingBufferCount, bDecoupleMode, sfDuration, vsyncPeriod)); mFrameByVsyncSf.emplace(vsyncSfTime, vecLayerFrameNum); } } void queueFence(const sp& fence, const nsecs_t vsyncSfTime = 0) { char message[64]; int err = 0; std::lock_guard lock(mMutex); if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { err = snprintf(message, sizeof(message), "%s fence %d has signaled", mName, fence->get()); if (err < 0) { ATRACE_NAME("SNPF1 error"); } else { ATRACE_NAME(message); } // Need an increment on both to make the trace number correct. return; } err = snprintf(message, sizeof(message), "Trace %s fence %d", mName, fence->get()); if (err < 0) { ATRACE_NAME("SNPF2 error"); } else { ATRACE_NAME(message); } mQueue.push_back(std::make_pair(fence, vsyncSfTime)); mCondition.notify_one(); } private: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-noreturn" void loop() { struct sched_attr { uint32_t size; uint32_t sched_policy; uint64_t sched_flags; int32_t sched_nice; uint32_t sched_priority; uint64_t sched_runtime; uint64_t sched_deadline; uint64_t sched_period; uint32_t sched_util_min; uint32_t sched_util_max; }; sched_attr attr = {}; attr.size = sizeof(attr); attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP); attr.sched_util_min = 0; attr.sched_util_max = 1024; attr.sched_priority = SCHED_RR; syscall(__NR_sched_setattr, 0, &attr, 0); while (true) { threadLoop(); } } #pragma clang diagnostic pop void threadLoop() { sp fence; nsecs_t vsyncSfTime = 0; std::vector> vecLayerFrameNum; { std::unique_lock lock(mMutex); while (mQueue.empty()) { mCondition.wait(lock); } fence = mQueue[0].first; vsyncSfTime = mQueue[0].second; } { std::lock_guard lock(mMutex); auto it = mFrameByVsyncSf.find(vsyncSfTime); if (it != mFrameByVsyncSf.end()) { vecLayerFrameNum = it->second; } } char message[64]; int err = snprintf(message, sizeof(message), "waiting for %s %d", mName, fence->get()); if (err < 0) { ATRACE_NAME("SNPF1 error"); } else { ATRACE_NAME(message); status_t result = fence->waitForever(message); if (result != OK) { ALOGE("Error waiting for fence: %d", result); } String8 strOutput; auto now = systemTime(); auto fenceSignaledTime = fence->getSignalTime(); if (isSupportMBrain() && vsyncSfTime > 0) { int touchDebugEnabled = 0; for (auto [name, frame, pending, decouple, sfDuration, vsyncPeriod] : vecLayerFrameNum) { strOutput.clear(); strOutput.appendFormat("%s:%d, now:%" PRId64 ", signaled:%" PRId64, mName, fence->get(), now, fenceSignaledTime); strOutput.appendFormat(", vsync-sf:%" PRId64, vsyncSfTime); strOutput.appendFormat(", (%s,%" PRIu64 ",%d,%d)", name.c_str(), frame, pending, decouple); strOutput.appendFormat(", T4(sf2now):%" PRId64 ", T4(sf2signaled):%" PRId64, now-vsyncSfTime, fenceSignaledTime-vsyncSfTime); strOutput.appendFormat(", sfDuration:%" PRId64 ", vsyncPeriod:%" PRId64, sfDuration, vsyncPeriod); bool bAbnormal = false; if (decouple) { bAbnormal = ((now - vsyncSfTime) > (sfDuration + (vsyncPeriod * 3 / 2))); } else { bAbnormal = ((now - vsyncSfTime) > (sfDuration + (vsyncPeriod / 2))); } strOutput.appendFormat(", abnormal:%d", bAbnormal ? 1 : 0); ATRACE_NAME(strOutput.c_str()); //ALOGI("%s", strOutput.c_str()); if (mMBGetTouchDebugEnabledFunc) { mMBGetTouchDebugEnabledFunc(touchDebugEnabled); } if (touchDebugEnabled != 0 && mMBnotifyT4Func) { mMBnotifyT4Func(const_cast(name.c_str()), frame, vsyncSfTime, now-vsyncSfTime, sfDuration, vsyncPeriod, bAbnormal ? 1 : 0, pending, decouple ? 1 : 0); } } } else { strOutput.appendFormat("%s:%d, now:%" PRId64 ", signaled:%" PRId64, mName, fence->get(), now, fenceSignaledTime); ATRACE_NAME(strOutput.c_str()); } } { std::lock_guard lock(mMutex); mQueue.pop_front(); mFrameByVsyncSf.erase(vsyncSfTime); } } const char* mName; std::deque, nsecs_t>> mQueue; std::condition_variable mCondition; std::mutex mMutex; std::unordered_map>> mFrameByVsyncSf GUARDED_BY(mMutex); void *mMBrainHandle = nullptr; MBNotifyT4 mMBnotifyT4Func = nullptr; MBGetTouchDebugEnabled mMBGetTouchDebugEnabledFunc = nullptr; }; public: static FenceTracer& getInstance() { static FenceTracer gInstance; return gInstance; } ~FenceTracer() {}; static FenceMonitor& getPresentFenceMonitor() { static FenceMonitor presentFenceMonitor("presentFence"); return presentFenceMonitor; } void trackFrameNumber(const std::string& layerName, const uint64_t frameNumber, const nsecs_t vsyncSfTime, const int32_t pendingBufferCount, const bool bDecoupleMode, const nsecs_t sfDuration, const nsecs_t vsyncPeriod) { if (!isSupportMBrain()) return; FenceTracer::getPresentFenceMonitor().queueFrameNumber(layerName, frameNumber, vsyncSfTime, pendingBufferCount, bDecoupleMode, sfDuration, vsyncPeriod); } void trackPresentFence(const sp& fence, const nsecs_t vsyncSfTime) { if (!ATRACE_ENABLED() && !isSupportMBrain()) return; FenceTracer::getPresentFenceMonitor().queueFence(fence, vsyncSfTime); } void trackScreenCaptureFence(const sp& fence) { static FenceMonitor trackScreenCaptureFence("screenCaptureFence"); trackScreenCaptureFence.queueFence(fence); } void trackWfdPresentFence(const sp& fence) { static FenceMonitor trackWfdPresentFence("WFD-Present"); trackWfdPresentFence.queueFence(fence); } }; // --------------------------------------------------------------------------- }; // namespace android