unplugged-system/frameworks/native/services/surfaceflinger/mediatek/FenceTracer.h

309 lines
14 KiB
C
Raw Normal View History

/* 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 <log/log.h>
#include <thread>
#include <utils/Trace.h>
#include <utils/Mutex.h>
#include <sys/syscall.h>
#include <android-base/properties.h>
#include <dlfcn.h>
#include <cutils/properties.h>
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<MBNotifyT4>(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<MBGetTouchDebugEnabled>(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<std::mutex> 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<std::tuple<std::string, uint64_t, int32_t, bool, int64_t, int64_t>> vecLayerFrameNum;
vecLayerFrameNum.push_back(std::make_tuple(layerName, frameNumber, pendingBufferCount, bDecoupleMode, sfDuration, vsyncPeriod));
mFrameByVsyncSf.emplace(vsyncSfTime, vecLayerFrameNum);
}
}
void queueFence(const sp<Fence>& fence, const nsecs_t vsyncSfTime = 0) {
char message[64];
int err = 0;
std::lock_guard<std::mutex> 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> fence;
nsecs_t vsyncSfTime = 0;
std::vector<std::tuple<std::string, uint64_t, int32_t, bool, int64_t, int64_t>> vecLayerFrameNum;
{
std::unique_lock<std::mutex> lock(mMutex);
while (mQueue.empty()) {
mCondition.wait(lock);
}
fence = mQueue[0].first;
vsyncSfTime = mQueue[0].second;
}
{
std::lock_guard<std::mutex> 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<char*>(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<std::mutex> lock(mMutex);
mQueue.pop_front();
mFrameByVsyncSf.erase(vsyncSfTime);
}
}
const char* mName;
std::deque<std::pair<sp<Fence>, nsecs_t>> mQueue;
std::condition_variable mCondition;
std::mutex mMutex;
std::unordered_map<nsecs_t, std::vector<std::tuple<std::string, uint64_t, int32_t, bool, int64_t, int64_t>>>
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>& fence, const nsecs_t vsyncSfTime) {
if (!ATRACE_ENABLED() && !isSupportMBrain()) return;
FenceTracer::getPresentFenceMonitor().queueFence(fence, vsyncSfTime);
}
void trackScreenCaptureFence(const sp<Fence>& fence) {
static FenceMonitor trackScreenCaptureFence("screenCaptureFence");
trackScreenCaptureFence.queueFence(fence);
}
void trackWfdPresentFence(const sp<Fence>& fence) {
static FenceMonitor trackWfdPresentFence("WFD-Present");
trackWfdPresentFence.queueFence(fence);
}
};
// ---------------------------------------------------------------------------
}; // namespace android