[6.2.0][技术优化] 优化各线程cpu使用的锁相关逻辑
This commit is contained in:
@@ -12,8 +12,8 @@ import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider;
|
||||
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
|
||||
import com.mogo.eagle.core.function.main.ARouterUtils;
|
||||
import com.zhjt.service.chain.ChainLog;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
|
||||
@AutoService(IHookInvoker.class)
|
||||
@@ -22,7 +22,6 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
private final Looper mainLooper = Looper.getMainLooper();
|
||||
|
||||
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
|
||||
private final AtomicLong syncLockStartTime = new AtomicLong(0L);
|
||||
|
||||
private final ThreadLocal<StringBuilder> message = new ThreadLocal<>();
|
||||
|
||||
@@ -30,16 +29,12 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
|
||||
private final AtomicReference<Thread> holder = new AtomicReference<>();
|
||||
|
||||
private String holderDesc = null;
|
||||
private final StringBuilder extra = new StringBuilder();
|
||||
|
||||
private volatile IMoGoCpuUsageProvider provider;
|
||||
|
||||
private volatile boolean getProviderRequested = false;
|
||||
|
||||
private final long logThreshold = 5; //日志打印阈值
|
||||
|
||||
private final long dumpStackThreshold = 20; // dump堆栈阈值
|
||||
|
||||
private volatile boolean isCanDump = false; // 是否可以Dump堆栈,加此标记位是防止应用启动过程中,由于dump主线程堆栈导致启动耗时
|
||||
|
||||
// 切记: 请勿在此方法中调用其它模块类中的api,可能会出现StackOverFlowException
|
||||
@@ -47,7 +42,12 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
public void i(Type type, Object caller,String methodName, Object... objects) {
|
||||
startTime.set(SystemClock.elapsedRealtime());
|
||||
boolean isMainThread = mainLooper == Looper.myLooper();
|
||||
handleSynchronizedLock(isMainThread, caller, methodName, objects);
|
||||
if (type == Type.SYNCHRONIZED_LOCK) {
|
||||
handleSynchronizedLock(isMainThread, caller, methodName, objects);
|
||||
}
|
||||
if (type == Type.AQS_LOCK) {
|
||||
handleAqsLockEnterBefore(isMainThread, caller, methodName, objects);
|
||||
}
|
||||
if (!getProviderRequested && provider == null && mainLooper != Looper.myLooper() && ARouterUtils.isInit.get()) {
|
||||
getProviderRequested = true;
|
||||
new Thread(() -> {
|
||||
@@ -70,19 +70,22 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
|
||||
// 切记: 请勿在此方法中调用其它模块类中的api,可能会出现StackOverFlowException
|
||||
@Override
|
||||
public void o(Type type, Object caller,String methodName, Object... objects) {
|
||||
public void o(Type type, Object caller,String methodName, Object... args) {
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
Long old = startTime.get();
|
||||
long cost = now - (old == null ? now : old);
|
||||
if (mainLooper == Looper.myLooper() && type != Type.SYNCHRONIZED_LOCK) {
|
||||
handleCostTimeRecord(type, caller, methodName, cost, objects);
|
||||
boolean isMainThread = mainLooper == Looper.myLooper();
|
||||
if (isMainThread && type != Type.SYNCHRONIZED_LOCK) {
|
||||
handleCostTimeRecord(type, null, caller, methodName, cost, args);
|
||||
}
|
||||
if (mainLooper == Looper.myLooper() && type == Type.ACTIVITY) {
|
||||
handleActivity((Activity) caller, methodName);
|
||||
if (isMainThread && type == Type.ACTIVITY) {
|
||||
handleActivity((Activity) caller, methodName, args);
|
||||
}
|
||||
if (type == Type.AQS_LOCK) {
|
||||
handleAqsLockEnterAfter(isMainThread, caller, methodName, args);
|
||||
}
|
||||
|
||||
if (provider != null) {
|
||||
if (mainLooper == Looper.myLooper()) {
|
||||
if (isMainThread) {
|
||||
provider.updateMainThreadTime();
|
||||
} else {
|
||||
provider.updateOtherThreadTime();
|
||||
@@ -90,7 +93,32 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCostTimeRecord(Type type, Object caller, String methodName, long cost, Object... args) {
|
||||
private void handleAqsLockEnterBefore(boolean isMainThread, Object caller, String methodName, Object[] objects) {
|
||||
if (isMainThread && caller instanceof Lock) {
|
||||
if ("lock".equals(methodName) || "lockInterruptibly".equals(methodName)) {
|
||||
if (extra.length() > 0) {
|
||||
extra.setLength(0);
|
||||
}
|
||||
extra.append(caller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAqsLockEnterAfter(boolean isMainThread, Object caller, String methodName, Object[] objects) {
|
||||
if (isMainThread && caller instanceof Lock) {
|
||||
if ("lock".equals(methodName) || "lockInterruptibly".equals(methodName)) {
|
||||
Long start = startTime.get();
|
||||
if (start != null) {
|
||||
long dur = SystemClock.elapsedRealtime() - start;
|
||||
handleCostTimeRecord(Type.AQS_LOCK, extra, caller, methodName, dur, objects);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCostTimeRecord(Type type, StringBuilder extra, Object caller, String methodName, long cost, Object... args) {
|
||||
//日志打印阈值
|
||||
long logThreshold = 5;
|
||||
if (cost >= logThreshold) {
|
||||
StringBuilder builder = message.get();
|
||||
if (builder == null) {
|
||||
@@ -123,6 +151,13 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
}
|
||||
|
||||
builder.append("#").append(cost);
|
||||
if (extra != null && extra.length() > 0) {
|
||||
builder.append("#");
|
||||
builder.append(extra);
|
||||
extra.setLength(0);
|
||||
}
|
||||
// dump堆栈阈值
|
||||
long dumpStackThreshold = 20;
|
||||
if (cost >= dumpStackThreshold && isCanDump) {
|
||||
builder.append("\n");
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
@@ -131,13 +166,13 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
}
|
||||
builder.setLength(builder.length() - 1);
|
||||
}
|
||||
Log.w("HookHandler", "Jank Detected:" + builder);
|
||||
Log.w("HookHandler", "Junk Detected:" + builder);
|
||||
linkedLog(type, builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleActivity(Activity caller, String methodName) {
|
||||
private void handleActivity(Activity caller, String methodName, Object... args) {
|
||||
if ("onCreate".equals(methodName)) {
|
||||
try {
|
||||
IMoGoBlockProvider block = CallerDevaToolsManager.INSTANCE.block();
|
||||
@@ -157,8 +192,14 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
if (!isCanDump) {
|
||||
isCanDump = true;
|
||||
}
|
||||
|
||||
if ("onWindowFocusChanged".equals(methodName)) {
|
||||
if (args.length > 0) {
|
||||
boolean hasFocus = (boolean)args[0];
|
||||
if (hasFocus && !isCanDump) {
|
||||
isCanDump = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,70 +229,38 @@ public class HookInvokerImpl implements IHookInvoker {
|
||||
private void handleSynchronizedLock(boolean isMainThread,Object caller, String methodName, Object [] objects) {
|
||||
if (isMainThread) {
|
||||
if ("onMonitorBefore".equals(methodName)) {
|
||||
syncLockStartTime.set(SystemClock.elapsedRealtime());
|
||||
Object monitor = null;
|
||||
if (objects.length > 0) {
|
||||
this.monitor.set(objects[0]);
|
||||
this.monitor.set(monitor = objects[0]);
|
||||
}
|
||||
Thread holder = this.holder.get();
|
||||
if (holder != null) {
|
||||
holderDesc = holder.toString();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (monitor != null) {
|
||||
sb.append("monitor::").append(monitor.getClass().getName());
|
||||
}
|
||||
if (holder != null) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("##holder::").append(holder);
|
||||
} else {
|
||||
sb.append("holder::").append(holder);
|
||||
}
|
||||
}
|
||||
if (extra.length() > 0) {
|
||||
extra.setLength(0);
|
||||
}
|
||||
extra.append(sb);
|
||||
}
|
||||
if ("onMonitorEnter".equals(methodName)) {
|
||||
long elapsedTime = SystemClock.elapsedRealtime() - syncLockStartTime.get();
|
||||
if (elapsedTime >= logThreshold) {
|
||||
StringBuilder builder = message.get();
|
||||
if (builder == null) {
|
||||
builder = new StringBuilder();
|
||||
message.set(builder);
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
builder.setLength(0);
|
||||
}
|
||||
builder
|
||||
.append(Type.SYNCHRONIZED_LOCK)
|
||||
.append("#")
|
||||
.append(caller == null ? "caller is null" : caller.getClass().getSimpleName())
|
||||
.append("#")
|
||||
.append(methodName);
|
||||
if (objects.length > 0) {
|
||||
builder.append("#(");
|
||||
}
|
||||
for (Object o : objects) {
|
||||
if (o == null) {
|
||||
continue;
|
||||
}
|
||||
builder.append(o.getClass().getSimpleName())
|
||||
.append(",");
|
||||
}
|
||||
|
||||
if (objects.length > 0) {
|
||||
builder.setLength(builder.length() - 1);
|
||||
builder.append(")");
|
||||
}
|
||||
builder.append("#").append(elapsedTime);
|
||||
Object monitor = this.monitor.get();
|
||||
if (monitor != null) {
|
||||
builder.append("#").append("monitor->").append(monitor.getClass().getName()).append("@").append(monitor.hashCode());
|
||||
}
|
||||
if (holderDesc != null) {
|
||||
builder.append("#").append("holder->").append(holderDesc);
|
||||
}
|
||||
if (elapsedTime >= dumpStackThreshold && isCanDump) {
|
||||
builder.append("\n");
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
for (StackTraceElement trace: stackTrace) {
|
||||
builder.append(trace.getClassName()).append("#").append(trace.getMethodName()).append("#").append(trace.getLineNumber()).append("\n");
|
||||
}
|
||||
builder.setLength(builder.length() - 1);
|
||||
}
|
||||
Log.w("HookHandler", "Jank detected:" + builder);
|
||||
linkedLog(Type.SYNCHRONIZED_LOCK, builder.toString());
|
||||
Long startTime = this.startTime.get();
|
||||
if (startTime == null) {
|
||||
return;
|
||||
}
|
||||
long cost = SystemClock.elapsedRealtime() - startTime;
|
||||
handleCostTimeRecord(Type.SYNCHRONIZED_LOCK, extra, caller, methodName, cost, objects);
|
||||
}
|
||||
if ("onMonitorExit".equals(methodName)) {
|
||||
this.monitor.remove();
|
||||
holderDesc = null;
|
||||
}
|
||||
} else {
|
||||
if ("onMonitorBefore".equals(methodName)) {
|
||||
|
||||
Reference in New Issue
Block a user