From 1dcdac00159fb032437ea90b684f826886653878 Mon Sep 17 00:00:00 2001 From: renwj Date: Wed, 15 Nov 2023 16:48:20 +0800 Subject: [PATCH] =?UTF-8?q?[6.2.0][=E6=8A=80=E6=9C=AF=E4=BC=98=E5=8C=96]?= =?UTF-8?q?=20=E6=89=93=E5=BC=80=E7=9B=91=E6=B5=8B=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../launcher/lancet/jank/MainBlockCheck.kt | 576 ++++++++++-------- .../jank/compiler/JankPointAutoGenerator.java | 56 ++ .../launcher/lancet/jank/spi/HookHandler.java | 82 ++- .../mogo/launcher/startup/ARouterStartUp.kt | 2 + build.gradle | 2 +- config.gradle | 4 +- .../DevaToolsProvider.kt | 6 + .../block/MainBlockLinkedLog.kt | 13 + .../block/MoGoBlockProviderImpl.kt | 17 +- .../perf/MoGoCpuUsageProviderImpl.kt | 81 +++ .../main/threadopt/ThreadOptInitializer.java | 45 +- .../api/devatools/IDevaToolsProvider.kt | 6 + .../devatools/perf/IMoGoCpuUsageProvider.kt | 14 + .../call/devatools/CallerDevaToolsManager.kt | 3 + gradle/bytex/bytex_lancetx.gradle | 7 +- 15 files changed, 622 insertions(+), 292 deletions(-) create mode 100644 core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/perf/MoGoCpuUsageProviderImpl.kt create mode 100644 core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/perf/IMoGoCpuUsageProvider.kt diff --git a/app/src/main/java/com/mogo/launcher/lancet/jank/MainBlockCheck.kt b/app/src/main/java/com/mogo/launcher/lancet/jank/MainBlockCheck.kt index ecc3b2ab96..a6e52577db 100644 --- a/app/src/main/java/com/mogo/launcher/lancet/jank/MainBlockCheck.kt +++ b/app/src/main/java/com/mogo/launcher/lancet/jank/MainBlockCheck.kt @@ -4,6 +4,8 @@ import android.app.* import android.content.* import android.graphics.* import android.os.* +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Handler.Callback import android.util.* import android.view.* @@ -21,7 +23,6 @@ import com.mogo.eagle.core.function.call.devatools.* import com.mogo.eagle.core.utilcode.util.* import kotlinx.coroutines.Runnable import java.lang.ref.WeakReference -import java.util.LinkedList import java.util.concurrent.* import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.withLock @@ -36,65 +37,61 @@ class MainBlockCheck { private const val TAG = "MAIN_BLOCK_CHECK" - @JvmStatic - private val tokens = ConcurrentHashMap>(128) + private val handlerTokens by lazy { ConcurrentHashMap>() } - private val pool by lazy { Executors.newFixedThreadPool(2) } + private val pool by lazy { Pools.SimplePool(50) } - val asyncHandlers by lazy { CopyOnWriteArraySet>() } - - private val handlersMap by lazy { ConcurrentHashMap() } - - private val whats by lazy { ConcurrentHashMap>>() } - - private val actionPool by lazy { Pools.SimplePool(500) } + private val actions by lazy { ConcurrentHashMap() } private val lock by lazy { ReentrantReadWriteLock() } - private data class PoolAction(var type: Int, var what: Int, var holder: Holder?, var elapsedTime: Long = -1, var duration:Long = -1): Runnable { + val asyncHandlers by lazy { CopyOnWriteArraySet>() } - override fun run() { - try { - val h = holder - if (type == 0 && h != null) { - whats.getOrPut(what) { LinkedList>() }.add( - WeakReference(h) - ) - getRecorder()?.insert(h) - } - if (type == 1) { - val holders = whats.remove(what)?.mapNotNull { it.get() } - if (!holders.isNullOrEmpty()) { - getRecorder()?.remove(holders) - } - } - if (type == 2 && h != null) { - val what = h.what - if (what != null) { - whats.remove(what)?.removeAll { it.get() == h } - } - getRecorder()?.recycle(h.id, elapsedTime, duration) - } - } finally { - reset() - lock.writeLock().withLock { - actionPool.release(this) - } - } - } + private val handlers by lazy { ConcurrentHashMap() } - fun reset() { - type = -1 - what = Int.MIN_VALUE - holder = null - elapsedTime = -1 - duration = -1 + private val handlerWhats by lazy { ConcurrentHashMap>() } + + private val main by lazy { Looper.getMainLooper() } + + private fun acquireAction(msg: Holder, action: Runnable): ActionWrapper { + return lock.readLock().withLock { + val ret = pool.acquire() ?: ActionWrapper(msg, action) + ret.msg = msg + ret.action = action + ret } } + private fun releaseAction(wrapper: ActionWrapper) { + lock.writeLock().withLock { pool.release(wrapper) } + } + private data class ActionWrapper(var msg: Holder, var action: Runnable) : Runnable { + override fun run() { + var elapsedTime: Long = 0 + var startTime: Long = 0 + try { + val expect = msg.enqueueTime + msg.delay + val now = now() + elapsedTime = now - expect + startTime = now() + action.run() + } catch (t: Throwable) { + Log.e(TAG, "msg -> $msg", t) + throw t + } finally { + getRecorder()?.recycle(msg.id, elapsedTime, now() - startTime) + releaseAction(this) + actions.remove(action) + CallerDevaToolsManager.usage()?.updateMainThreadTime(Debug.threadCpuTimeNanos()) + } + } + + override fun toString(): String { + return action.toString() + } + } - private fun getAction(): PoolAction = lock.readLock().withLock { actionPool.acquire() ?: PoolAction(-1, 0, null) } @JvmStatic fun getRecorder(): IMessageRecorder? { @@ -121,8 +118,7 @@ class MainBlockCheck { val action = enclose.action val oldMsg = enclose.origin val oldObj = enclose.obj - val now = now() - val elapsedTime = now - enclose.enqueueTime + val start = now() try { if (action != null) { action.run() @@ -133,44 +129,48 @@ class MainBlockCheck { oldMsg.obj = oldObj h?.dispatchMessage(oldMsg) } + if (h != null) { + val key = generateKey(h, msg.what) + handlerTokens[key]?.remove(enclose) + } } } catch (t: Throwable) { - Log.e(TAG, "handler: $h -> ${handlersMap[h.toString()]} -> msg: $enclose", t) + Log.e(TAG, "msg: $enclose", t) throw t } finally { - val duration = now() - now - recycle(enclose, elapsedTime, duration) + val now = now() + val duration = now - start + val expect = enclose.enqueueTime + enclose.delay + val elapsed = now - expect + getRecorder()?.recycle(enclose.id, elapsed, duration) + CallerDevaToolsManager.usage()?.updateMainThreadTime(Debug.threadCpuTimeNanos()) } true } } @JvmStatic val ASYNC_HANDLER by lazy { - HandlerCompat.createAsync(Looper.getMainLooper(), callback) + HandlerCompat.createAsync(main, callback) } @JvmStatic val HANDLER by lazy { - Handler(Looper.getMainLooper(), callback) + Handler(main, callback) } @JvmStatic @TargetClass(value = "android.os.Handler", scope = ALL) @TargetMethod(methodName = "post") @ReplaceInvoke - fun handlerPostProxy(handler: Handler, action: Runnable): Boolean { - if (Looper.getMainLooper() != handler.looper) { + fun handlerPost(handler: Handler, action: Runnable): Boolean { + if (main != handler.looper) { return handler.post(action) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - val msg = Message.obtain(newHandler) - val what = ObjectHashCodeUtils.getHashCodeIfNeed(newHandler, action) - msg.what = what - val holder = Holder.acquire(newHandler, action = action, isAsync = isAsync) - msg.obj = holder - val inserted = newHandler.sendMessage(msg) + val holder = Holder.acquire(handler, action = action, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }) + val inserted = handler.post(acquireAction(holder, action).also { actions[action] = it }) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) + } else { + actions.remove(action) } return inserted } @@ -179,47 +179,34 @@ class MainBlockCheck { @TargetClass(value = "android.os.Handler", scope = ALL) @TargetMethod(methodName = "postDelayed") @ReplaceInvoke - fun handlerPostDelayProxy(handler: Handler, action: Runnable, delayMillis: Long): Boolean { - if (Looper.getMainLooper() != handler.looper) { + fun handlerPostDelay(handler: Handler, action: Runnable, delayMillis: Long): Boolean { + if (main != handler.looper) { return handler.postDelayed(action, delayMillis) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - val msg = Message.obtain(newHandler) - val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - msg.what = what - val holder = Holder.acquire(handler, action = action, delay = delayMillis, isAsync = isAsync) - msg.obj = holder - val inserted = newHandler.sendMessageDelayed(msg, delayMillis) + val holder = Holder.acquire(handler, action = action, delay = delayMillis, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }) + val inserted = handler.postDelayed(acquireAction(holder, action).also { actions[action] = it }, delayMillis) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) + } else { + actions.remove(action) } return inserted } @JvmStatic @TargetClass(value = "android.os.Handler", scope = ALL) - @TargetMethod(methodName = "postAtTime") + @TargetMethod(methodName = "postDelayed") @ReplaceInvoke - fun handlerPostDelayProxy2(handler: Handler, action: Runnable, token: Any?, delayMillis: Long): Boolean { - if (Looper.getMainLooper() != handler.looper) { + fun handlerPostDelayWithToken(handler: Handler, action: Runnable, token: Any?, delayMillis: Long): Boolean { + if (main != handler.looper) { return HandlerCompat.postDelayed(handler, action, token, delayMillis) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - val msg = Message.obtain(newHandler) - val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - msg.what = what - val holder = Holder.acquire(handler, action = action, delay = delayMillis, obj = token) - msg.obj = holder - if (token != null) { - synchronized(tokens) { - tokens[token.hashCode()] = WeakReference(holder) - } - } - val inserted = HandlerCompat.postDelayed(newHandler, action, token, delayMillis) + val holder = Holder.acquire(handler, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }, action = action, obj = token, delay = delayMillis) + val inserted = HandlerCompat.postDelayed(handler, acquireAction(holder, action).also { actions[action] = it }, token, delayMillis) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) + } else { + actions.remove(action) } return inserted } @@ -228,20 +215,16 @@ class MainBlockCheck { @TargetClass(value = "android.os.Handler", scope = ALL) @TargetMethod(methodName = "postAtFrontOfQueue") @ReplaceInvoke - fun handlerPostAtFrontOfQueueProxy(handler: Handler, action: Runnable): Boolean { - if (Looper.getMainLooper() != handler.looper) { + fun handlerPostAtFrontOfQueue(handler: Handler, action: Runnable): Boolean { + if (main != handler.looper) { return handler.postAtFrontOfQueue(action) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - val msg = Message.obtain(newHandler) - val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - msg.what = what - val holder = Holder.acquire(handler, action = action) - msg.obj = holder - val inserted = newHandler.sendMessageAtFrontOfQueue(msg) + val holder = Holder.acquire(handler, action = action, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }) + val inserted = handler.postAtFrontOfQueue(acquireAction(holder, action).also { actions[action] = it }) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) + } else { + actions.remove(action) } return inserted } @@ -250,21 +233,17 @@ class MainBlockCheck { @TargetClass(value = "android.os.Handler", scope = ALL) @TargetMethod(methodName = "postAtTime") @ReplaceInvoke - fun handlerPostAtTimeProxy(handler: Handler, action: Runnable, uptimeMillis: Long): Boolean { - if (Looper.getMainLooper() != handler.looper) { + fun handlerPostAtTime(handler: Handler, action: Runnable, uptimeMillis: Long): Boolean { + if (main != handler.looper) { return handler.postAtTime(action, uptimeMillis) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - val msg = Message.obtain(newHandler) - val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - msg.what = what val delta = uptimeMillis - SystemClock.uptimeMillis() - val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0) - msg.obj = holder - val inserted = newHandler.sendMessageAtTime(msg, uptimeMillis) + val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }) + val inserted = handler.postAtTime(acquireAction(holder, action), uptimeMillis) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) + } else { + actions.remove(action) } return inserted } @@ -273,26 +252,17 @@ class MainBlockCheck { @TargetClass(value = "android.os.Handler", scope = ALL) @TargetMethod(methodName = "postAtTime") @ReplaceInvoke - fun handlerPostAtTimeProxy2(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long): Boolean { - if (Looper.getMainLooper() != handler.looper) { + fun handlerPostAtTimeWithToken(handler: Handler, action: Runnable, token: Any?, uptimeMillis: Long): Boolean { + if (main != handler.looper) { return handler.postAtTime(action, uptimeMillis) } - val isAsync = handlersMap.getOrPut(handler.toString() ) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - val msg = Message.obtain(newHandler) - val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - msg.what = what val delta = uptimeMillis - SystemClock.uptimeMillis() - val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, obj = token) - msg.obj = holder - if (token != null) { - synchronized(tokens) { - tokens[token.hashCode()] = WeakReference(holder) - } - } - val inserted = newHandler.sendMessageAtTime(msg, uptimeMillis) + val holder = Holder.acquire(handler, action = action, delay = if (delta > 0) delta else 0, obj = token, isAsync = handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }) + val inserted = handler.postAtTime(acquireAction(holder, action).also { actions[action] = it }, token, uptimeMillis) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) + } else { + actions.remove(action) } return inserted } @@ -302,17 +272,23 @@ class MainBlockCheck { @TargetMethod(methodName = "sendEmptyMessage") @ReplaceInvoke fun handlerSendEmptyMessage(handler: Handler, what: Int): Boolean { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.sendEmptyMessage(what) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } + val key = handler.hashCode() + val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) } val newHandler = getHandler(isAsync) val msg = Message.obtain(newHandler, what) val holder = Holder.acquire(handler, what = what, isAsync = isAsync) msg.obj = holder + val tokenKey = generateKey(handler, what) + handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder) val inserted = newHandler.sendMessage(msg) if (inserted) { - insert(what, holder) + handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what) + getRecorder()?.insert(holder) + } else { + handlerTokens[tokenKey]?.remove(holder) } return inserted } @@ -322,18 +298,24 @@ class MainBlockCheck { @TargetMethod(methodName = "sendEmptyMessageAtTime") @ReplaceInvoke fun handlerSendEmptyMessageAtTime(handler: Handler, what: Int, uptimeMillis: Long): Boolean { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.sendEmptyMessageAtTime(what, uptimeMillis) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } + val key = handler.hashCode() + val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) } val newHandler = getHandler(isAsync) val msg = Message.obtain(newHandler, what) val delta = uptimeMillis - SystemClock.uptimeMillis() val holder = Holder.acquire(handler, what = what, delay = if (delta > 0) delta else 0, isAsync = isAsync) msg.obj = holder + val tokenKey = generateKey(handler, what) + handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder) val inserted = newHandler.sendMessageAtTime(msg, uptimeMillis) if (inserted) { - insert(what, holder) + handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what) + getRecorder()?.insert(holder) + } else { + handlerTokens[tokenKey]?.remove(holder) } return inserted } @@ -343,17 +325,23 @@ class MainBlockCheck { @TargetMethod(methodName = "sendEmptyMessageDelayed") @ReplaceInvoke fun handlerSendEmptyMessageDelayed(handler: Handler, what: Int, delayMillis: Long): Boolean { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.sendEmptyMessageDelayed(what, delayMillis) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } + val key = handler.hashCode() + val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) } val newHandler = getHandler(isAsync) val msg = Message.obtain(newHandler, what) val holder = Holder.acquire(handler, what = what, delay = delayMillis, isAsync = isAsync) msg.obj = holder + val tokenKey = generateKey(handler, what) + handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder) val inserted = newHandler.sendMessageDelayed(msg, delayMillis) if (inserted) { - insert(what, holder) + handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(what) + getRecorder()?.insert(holder) + } else { + handlerTokens[tokenKey]?.remove(holder) } return inserted } @@ -363,28 +351,34 @@ class MainBlockCheck { @TargetMethod(methodName = "sendMessage") @ReplaceInvoke fun handlerSendMessage(handler: Handler, msg: Message): Boolean { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.sendMessage(msg) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - var newMsg = msg + val tokenKey = generateKey(handler, msg.what) + val key = handler.hashCode() + val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) } val action = msg.callback + val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync) + val inserted: Boolean if (action != null) { - newMsg = Message.obtain(newHandler) - newMsg.what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) + inserted = handler.post(acquireAction(holder, action).also { actions[action] = it }) + } else { + msg.obj = holder + handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder) + val newHandler = getHandler(isAsync) + inserted = newHandler.sendMessage(msg) } - val obj: Holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync) - val oldObj = msg.obj - newMsg.obj = obj - if (oldObj != null) { - synchronized(tokens) { - tokens[oldObj.hashCode()] = WeakReference(obj) - } - } - val inserted = newHandler.sendMessage(newMsg) if (inserted) { - insert(newMsg.what, obj) + if (action == null) { + handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what) + } + getRecorder()?.insert(holder) + } else { + if (action == null) { + handlerTokens[tokenKey]?.remove(holder) + } else { + actions.remove(action) + } } return inserted } @@ -394,29 +388,35 @@ class MainBlockCheck { @TargetMethod(methodName = "sendMessageAtTime") @ReplaceInvoke fun handlerSendMessageAtTime(handler: Handler, msg: Message, uptimeMillis: Long): Boolean { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.sendMessageAtTime(msg, uptimeMillis) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - var newMsg = msg + val tokenKey = generateKey(handler, msg.what) + val key = handler.hashCode() + val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) } val action = msg.callback - if (action != null) { - newMsg = Message.obtain(newHandler) - newMsg.what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - } val delta = uptimeMillis - SystemClock.uptimeMillis() - val obj= Holder.acquire(handler, msg, obj = msg.obj, action = msg.callback, what = msg.what, delay = if (delta > 0) delta else 0, isAsync = isAsync) - val oldObj = msg.obj - newMsg.obj = obj - if (oldObj != null) { - synchronized(tokens) { - tokens[oldObj.hashCode()] = WeakReference(obj) - } + val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync, delay = if (delta > 0) delta else 0) + val inserted: Boolean + if (action != null) { + inserted = handler.postAtTime(acquireAction(holder, action).also { actions[action] = it }, uptimeMillis) + } else { + msg.obj = holder + handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder) + val newHandler = getHandler(isAsync) + inserted = newHandler.sendMessageAtTime(msg, uptimeMillis) } - val inserted = newHandler.sendMessageAtTime(newMsg, uptimeMillis) if (inserted) { - insert(newMsg.what, obj) + if (action == null) { + handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what) + } + getRecorder()?.insert(holder) + } else { + if (action == null) { + handlerTokens[tokenKey]?.remove(holder) + } else { + actions.remove(action) + } } return inserted } @@ -426,46 +426,81 @@ class MainBlockCheck { @TargetMethod(methodName = "sendMessageAtFrontOfQueue") @ReplaceInvoke fun handlerSendMessageAtFrontOfQueue(handler: Handler, msg: Message): Boolean { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.sendMessageAtFrontOfQueue(msg) } - val isAsync = handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) } - val newHandler = getHandler(isAsync) - var newMsg = msg + val tokenKey = generateKey(handler, msg.what) + val key = handler.hashCode() + val isAsync = handlers.getOrPut(key) { computeIsAsyncHandler(handler) } val action = msg.callback + val holder = Holder.acquire(handler, originMsg = msg, action = msg.callback, obj = msg.obj, what = msg.what, isAsync = isAsync) + val inserted: Boolean if (action != null) { - newMsg = Message.obtain(newHandler) - newMsg.what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) + inserted = handler.postAtFrontOfQueue(acquireAction(holder, action).also { actions[action] = it }) + } else { + msg.obj = holder + handlerTokens.getOrPut(tokenKey) { CopyOnWriteArrayList() }.add(holder) + val newHandler = getHandler(isAsync) + inserted = newHandler.sendMessageAtFrontOfQueue(msg) } - val obj = Holder.acquire(handler, msg, obj = msg.obj, action = msg.callback, what = msg.what, isAsync = isAsync) - val old = msg.obj - newMsg.obj = obj - if (old != null) { - synchronized(tokens) { - tokens[old.hashCode()] = WeakReference(obj) - } - } - val inserted = newHandler.sendMessageAtFrontOfQueue(newMsg) if (inserted) { - insert(newMsg.what, obj) + if (action == null) { + handlerWhats.getOrPut(key) { CopyOnWriteArraySet() }.add(holder.what) + } + getRecorder()?.insert(holder) + } else { + if (action == null) { + handlerTokens[tokenKey]?.remove(holder) + } else { + actions.remove(action) + } } return inserted } + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "hasCallbacks") + @ReplaceInvoke + fun handlerHasCallbacks(handler: Handler, action: Runnable): Boolean { + if (main != handler.looper) { + return if (VERSION.SDK_INT >= VERSION_CODES.Q) { + handler.hasCallbacks(action) + } else { + HandlerCompat.hasCallbacks(handler, action) + } + } + val existed = actions[action] + if (existed != null) { + return HandlerCompat.hasCallbacks(handler, existed) + } + return false + } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "hasMessages") + @ReplaceInvoke + fun handlerHasMessages(handler: Handler, what: Int): Boolean { + if (main != handler.looper) { + return handler.hasMessages(what) + } + return getHandler(computeIsAsyncHandler(handler)).hasMessages(what) + } + + + @JvmStatic @TargetClass(value = "android.app.Activity", scope = ALL) @TargetMethod(methodName = "post") @ReplaceInvoke - fun runOnUiThreadOfActivityProxy(activity: Activity, action: Runnable) { - val msg = Message.obtain() - val what = ObjectHashCodeUtils.getHashCodeIfNeed(activity, action) - msg.what = what - val holder = Holder.acquire(null, action = action, what = msg.what, extra = mutableMapOf("runOnUiThread" to "${activity.javaClass.simpleName}#${action.javaClass.name}")) - msg.obj = holder - val inserted = HANDLER.sendMessage(msg) - if (inserted) { - insert(what, holder) + fun runOnUiThreadOfActivity(activity: Activity, action: Runnable) { + val holder = Holder.acquire(null, action = action) + try { + activity.runOnUiThread(acquireAction(holder, action)) + } finally { + getRecorder()?.insert(holder) } } @@ -473,15 +508,11 @@ class MainBlockCheck { @TargetClass(value = "android.view.View", scope = ALL) @TargetMethod(methodName = "post") @ReplaceInvoke - fun postOfViewProxy(view: View, action: Runnable): Boolean { - val msg = Message.obtain() - val what = ObjectHashCodeUtils.getHashCodeIfNeed(view, action) - msg.what = what - val holder = Holder.acquire(null, action = action, what = what, extra = mutableMapOf("post" to "${view.javaClass.simpleName}#${action.javaClass.name}")) - msg.obj = holder - val inserted = HANDLER.sendMessage(msg) + fun postOfView(view: View, action: Runnable): Boolean { + val holder = Holder.acquire(null, action = action) + val inserted = view.post(acquireAction(holder, action).also { actions[action] = it }) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) } return inserted } @@ -490,15 +521,11 @@ class MainBlockCheck { @TargetClass(value = "android.view.View", scope = ALL) @TargetMethod(methodName = "postDelayed") @ReplaceInvoke - fun postDelayedOfViewProxy(view: View, action: Runnable, delayMillis: Long): Boolean { - val msg = Message.obtain() - val what = ObjectHashCodeUtils.getHashCodeIfNeed(view, action) - msg.what = what - val holder = Holder.acquire(null, action = action, delay = delayMillis, what = what, extra = mutableMapOf("post" to "${view.javaClass.simpleName}#${action.javaClass.name}")) - msg.obj = holder - val inserted = HANDLER.sendMessageDelayed(msg, delayMillis) + fun postDelayedOfView(view: View, action: Runnable, delayMillis: Long): Boolean { + val holder = Holder.acquire(null, action = action, delay = delayMillis) + val inserted = view.postDelayed(acquireAction(holder, action).also { actions[action] = it }, delayMillis) if (inserted) { - insert(what, holder) + getRecorder()?.insert(holder) } return inserted } @@ -508,14 +535,16 @@ class MainBlockCheck { @TargetMethod(methodName = "removeCallbacks") @ReplaceInvoke fun handlerRemoveCallbacks(handler: Handler, action: Runnable) { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.removeCallbacks(action) } - val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - try { - return getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what) - } finally { - remove(what) + val existed = actions.remove(action) + if (existed != null) { + try { + handler.removeCallbacks(existed) + } finally { + getRecorder()?.remove(existed.msg) + } } } @@ -524,14 +553,17 @@ class MainBlockCheck { @TargetMethod(methodName = "removeCallbacks") @ReplaceInvoke fun handlerRemoveCallbacksWithToken(handler: Handler, action: Runnable, token: Any?) { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.removeCallbacks(action, token) } - val what = ObjectHashCodeUtils.getHashCodeIfNeed(handler, action) - getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what, if (token != null) { synchronized( - tokens - ) { tokens.remove(token.hashCode())?.get() } } else null) - remove(what) + val existed = actions.remove(action) + if (existed != null) { + try { + handler.removeCallbacks(existed, token) + } finally { + getRecorder()?.remove(existed.msg) + } + } } @JvmStatic @@ -539,22 +571,55 @@ class MainBlockCheck { @TargetMethod(methodName = "removeMessages") @ReplaceInvoke fun handlerRemoveMessages(handler: Handler, what: Int) { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.removeMessages(what) } - getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what) - remove(what) + val newHandler = getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }) + val tokens = handlerTokens.remove(generateKey(handler, what)) + if (!tokens.isNullOrEmpty()) { + val snapshot = ArrayList(tokens) + snapshot.forEach { + newHandler.removeMessages(what, it) + } + getRecorder()?.remove(snapshot) + } } + + @JvmStatic + @TargetClass(value = "android.os.Handler", scope = ALL) + @TargetMethod(methodName = "removeCallbacksAndMessages") + @ReplaceInvoke + fun handlerRemoveCallbacksAndMessages(handler: Handler, token: Any?) { + if (main != handler.looper) { + return handler.removeCallbacksAndMessages(token) + } + handler.removeCallbacksAndMessages(token) + val existed = handlerWhats[handler.hashCode()] + if (!existed.isNullOrEmpty()) { + existed.forEach {what -> + handlerTokens.remove(generateKey(handler, what))?.forEach { msg -> + getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }).removeMessages(what, msg) + } + } + } + } + + @JvmStatic @TargetClass(value = "android.view.View", scope = ALL) @TargetMethod(methodName = "removeCallbacks") @ReplaceInvoke fun viewRemoveCallback(view: View, action: Runnable): Boolean { - val what = ObjectHashCodeUtils.getHashCodeIfNeed(view, action) - HANDLER.removeMessages(what) - remove(what) - return true + val exist = actions.remove(action) + if (exist != null) { + try { + return view.removeCallbacks(exist) + } finally { + getRecorder()?.remove(exist.msg) + } + } + return false } @JvmStatic @@ -562,43 +627,28 @@ class MainBlockCheck { @TargetMethod(methodName = "removeMessages") @ReplaceInvoke fun handlerRemoveMessagesWithToken(handler: Handler, what: Int, token: Any?) { - if (Looper.getMainLooper() != handler.looper) { + if (main != handler.looper) { return handler.removeMessages(what, token) } - getHandler(handlersMap.getOrPut(handler.toString()) { computeIsAsyncHandler(handler) }).removeMessages(what, if (token != null) synchronized( - tokens - ) { tokens.remove(token.hashCode())?.get() } else null) - remove(what) + val newHandler = getHandler(handlers.getOrPut(handler.hashCode()) { computeIsAsyncHandler(handler) }) + val tokens = handlerTokens.remove(generateKey(handler, what)) + if (!tokens.isNullOrEmpty()) { + val snapshot = ArrayList(tokens) + snapshot.forEach { + newHandler.removeMessages(what, it) + } + getRecorder()?.remove(snapshot) + } + } + + private fun generateKey(handler: Handler, what: Int) : String { + return "${handler.hashCode()}_$what" } private fun computeIsAsyncHandler(handler: Handler): Boolean { return asyncHandlers.find { it.get() === handler } != null } - fun insert(what: Int, holder: Holder) { - val action = getAction() - action.type = 0 - action.what = what - action.holder = holder - pool.execute(action) - } - - private fun remove(what: Int) { - val action = getAction() - action.type = 1 - action.what = what - pool.execute(action) - } - - private fun recycle(holder: Holder, elapsedTime: Long, duration: Long) { - val action = getAction() - action.type = 2 - action.holder = holder - action.elapsedTime = elapsedTime - action.duration = duration - pool.execute(action) - } - private fun getHandler(isAsync: Boolean): Handler { return if (isAsync) { ASYNC_HANDLER diff --git a/app/src/main/java/com/mogo/launcher/lancet/jank/compiler/JankPointAutoGenerator.java b/app/src/main/java/com/mogo/launcher/lancet/jank/compiler/JankPointAutoGenerator.java index 778ed9eebf..5f1cf0811e 100644 --- a/app/src/main/java/com/mogo/launcher/lancet/jank/compiler/JankPointAutoGenerator.java +++ b/app/src/main/java/com/mogo/launcher/lancet/jank/compiler/JankPointAutoGenerator.java @@ -10,6 +10,8 @@ import android.content.ContentProvider; import android.content.pm.PackageManager; import android.os.SystemClock; import android.view.View; +import android.view.ViewManager; +import android.widget.Toast; import com.knightboost.lancet.api.synchronized_lock.ISynchronizedLockHooker; import com.mogo.core.lancetx.compiler.lib.annotations.LancetXGenerator; @@ -17,6 +19,8 @@ import com.mogo.core.lancetx.compiler.lib.generator.HookType; import com.mogo.core.lancetx.compiler.lib.generator.Scope; import com.mogo.core.lancetx.compiler.lib.generator.Type; +import java.io.File; +import java.io.InputStream; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.LockSupport; @@ -178,4 +182,56 @@ class JankPointAutoGenerator { excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" } ) private androidx.fragment.app.Fragment androidxFragment; + + + @LancetXGenerator( + group = "main_block_check", + type = Type.IO, + scope = Scope.ALL, + hookType = HookType.REPLACE_INVOKE, + hookAllPublicMethods = true, + excludeHookMethodNames = { "toString", "hashCode", "compareTo", "compareTo" }, + excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" , "(Ljava/io/File;)I", "(Ljava/lang/Object;)I" } + ) + private File file; + + + @LancetXGenerator( + group = "main_block_check", + type = Type.IO, + scope = Scope.ALL, + hookType = HookType.REPLACE_INVOKE, + hookAllPublicMethods = true, + excludeHookMethodNames = { "toString", "hashCode" }, + excludeHookMethodDescs = { "()Ljava/lang/String;", "()I" } + ) + private InputStream is; + + + @LancetXGenerator( + group = "main_block_check", + type = Type.VIEW, + scope = Scope.ALL, + hookType = HookType.REPLACE_INVOKE, + hookAllPublicMethods = true, + onlyHookMethodNames = { "show" }, + onlyHookMethodDescs = { "()V" } + ) + private Toast toast; + + + @LancetXGenerator( + group = "main_block_check", + type = Type.VIEW, + scope = Scope.ALL, + hookType = HookType.REPLACE_INVOKE, + hookAllPublicMethods = true, + onlyHookMethodNames = { "addView", "updateViewLayout", "removeView" }, + onlyHookMethodDescs = { + "(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V", + "(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V", + "(Landroid/view/View;)V" + } + ) + private ViewManager manager; } diff --git a/app/src/main/java/com/mogo/launcher/lancet/jank/spi/HookHandler.java b/app/src/main/java/com/mogo/launcher/lancet/jank/spi/HookHandler.java index 4a48f0e235..48c4aacbec 100644 --- a/app/src/main/java/com/mogo/launcher/lancet/jank/spi/HookHandler.java +++ b/app/src/main/java/com/mogo/launcher/lancet/jank/spi/HookHandler.java @@ -6,9 +6,9 @@ import androidx.annotation.NonNull; import com.google.auto.service.AutoService; import com.mogo.core.lancetx.compiler.lib.generator.Type; import com.mogo.core.lancetx.compiler.lib.hook.IHookInvoker; +import com.mogo.eagle.core.data.deva.chain.ChainConstant; +import com.zhjt.service.chain.ChainLog; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -25,12 +25,25 @@ public class HookHandler implements IHookInvoker { private final ThreadLocal message = new ThreadLocal<>(); + private final ThreadLocal monitor = new ThreadLocal<>(); + + private final AtomicReference holder = new AtomicReference<>(); + + private String holderDesc = null; + @Override public void i(@NonNull Type type, Object caller, @NonNull String methodName, @NonNull Object... objects) { if (Looper.myLooper() == mainLooper) { if (type == Type.SYNCHRONIZED_LOCK) { if ("onMonitorBefore".equals(methodName)) { syncLockStartTime.set(SystemClock.elapsedRealtime()); + if (objects.length > 0) { + this.monitor.set(objects[0]); + } + Thread holder = this.holder.get(); + if (holder != null) { + holderDesc = holder.toString(); + } } if ("onMonitorEnter".equals(methodName)) { long elapsedTime = SystemClock.elapsedRealtime() - syncLockStartTime.get(); @@ -64,25 +77,49 @@ public class HookHandler implements IHookInvoker { builder.setLength(builder.length() - 1); builder.append(")"); } + builder.append("#").append(elapsedTime); + Object monitor = this.monitor.get(); + if (monitor != null) { + builder.append("#").append(monitor); + } + if (holderDesc != null) { + builder.append("#").append(holderDesc); + } builder.append("\n"); StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); for (StackTraceElement trace: stackTrace) { - if ("dalvik.system.VMStack".equals(trace.getClassName()) && "getThreadStackTrace".equals(trace.getMethodName())) { - continue; - } - if ("java.lang.Thread".equals(trace.getClassName()) && "getStackTrace".equals(trace.getMethodName())) { - continue; - } 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); + Log.w("HookHandler", "Jank detected:" + builder); + linkedLog(type, builder.toString()); } } + if ("onMonitorExit".equals(methodName)) { + this.monitor.remove(); + holderDesc = null; + } } else { startTime.set(SystemClock.elapsedRealtime()); } + } else { + if (type == Type.SYNCHRONIZED_LOCK) { + if ("onMonitorBefore".equals(methodName)) { + if (objects.length > 0) { + this.monitor.set(objects[0]); + } + } + + if ("onMonitorEnter".equals(methodName)) { + holder.set(Thread.currentThread()); + } + + if ("onMonitorExit".equals(methodName)) { + this.monitor.remove(); + holder.set(null); + } + } } } @@ -122,25 +159,38 @@ public class HookHandler implements IHookInvoker { builder.setLength(builder.length() - 1); builder.append(")"); } + + builder.append("#").append(delta); if (type == Type.AQS_LOCK) { builder.append("\n"); StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); for (StackTraceElement trace: stackTrace) { - if ("dalvik.system.VMStack".equals(trace.getClassName()) && "getThreadStackTrace".equals(trace.getMethodName())) { - continue; - } - if ("java.lang.Thread".equals(trace.getClassName()) && "getStackTrace".equals(trace.getMethodName())) { - continue; - } 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); + Log.w("HookHandler", "Jank Detected:" + builder); + + linkedLog(type, builder.toString()); } } } } + private void linkedLog(Type type, String msg) { + try { + linkedLogInternal(type,msg); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + @ChainLog( + linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK, + linkCode = ChainConstant.CHAIN_SOURCE_HMI, + nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK, + paramIndexes = { 0, 1 } + ) + private void linkedLogInternal(Type type, String msg) {} } diff --git a/app/src/main/java/com/mogo/launcher/startup/ARouterStartUp.kt b/app/src/main/java/com/mogo/launcher/startup/ARouterStartUp.kt index 625d148601..0c54a60ac9 100644 --- a/app/src/main/java/com/mogo/launcher/startup/ARouterStartUp.kt +++ b/app/src/main/java/com/mogo/launcher/startup/ARouterStartUp.kt @@ -3,6 +3,7 @@ package com.mogo.launcher.startup import android.app.Application import com.alibaba.android.arouter.launcher.ARouter import com.mogo.commons.debug.DebugConfig +import com.mogo.eagle.core.function.main.threadopt.ThreadOptInitializer import com.mogo.eagle.core.utilcode.util.AppUtils import com.mogo.eagle.core.utilcode.util.CleanUtils import java.lang.Exception @@ -17,6 +18,7 @@ object ARouterStartUp { } // 初始化 arouter ARouter.init(app) + ThreadOptInitializer.aRouterInit.set(true) } catch (e: Exception) { e.printStackTrace() // 由于ARouter会在SP_AROUTER_CACHE.xml缓存路由表,如果出现了被删除的情况会报错,这里清除下就好了 diff --git a/build.gradle b/build.gradle index 2fae1bc804..94bf975c04 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ buildscript { classpath "com.mogo.cloud:systrace:${plugin_version}" classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18' classpath "com.mogo.sticky:service:${plugin_version}" - classpath "io.github.knight-zxw:lancet-plugin:10.3.0" + classpath "io.github.knight-zxw:lancet-plugin:10.50.0" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0" classpath 'com.mogo.cloud:matrix:1.0.2' classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513' diff --git a/config.gradle b/config.gradle index d9b18705da..ca7f0e0307 100644 --- a/config.gradle +++ b/config.gradle @@ -208,7 +208,7 @@ ext { //========================= LancetX =================== - lancetx_runtime : "io.github.knight-zxw:lancet-runtime:10.3.0", + lancetx_runtime : "io.github.knight-zxw:lancet-runtime:10.50.0", lancetx_compiler : "com.mogo.eagle.core.lancetx:compiler:1.0.0", @@ -229,7 +229,7 @@ ext { passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT", // 主线程卡顿监测 - block_detector : "com.mogo.eagle.core.block:runtime:10.20.0", + block_detector : "com.mogo.eagle.core.block:runtime:10.50.0", //======================== google auto-service =============== google_auto_service : "com.google.auto.service:auto-service:1.0-rc7" diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt index 03ab776345..000169af02 100644 --- a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/DevaToolsProvider.kt @@ -23,6 +23,7 @@ import com.mogo.eagle.core.function.api.devatools.strict.* import com.mogo.eagle.core.function.api.devatools.download.* import com.mogo.eagle.core.function.api.devatools.logcat.* import com.mogo.eagle.core.function.api.devatools.mofang.* +import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider import com.mogo.eagle.core.function.api.lookaround.* import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.w @@ -53,6 +54,7 @@ import com.zhjt.mogo_core_function_devatools.mofang.* import com.zhjt.mogo_core_function_devatools.monitor.MonitorManager import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb.Companion.getDb +import com.zhjt.mogo_core_function_devatools.perf.MoGoCpuUsageProviderImpl import com.zhjt.mogo_core_function_devatools.report.IPCReportManager.Companion.iPCReportManager import com.zhjt.mogo_core_function_devatools.scene.SceneManager.Companion.sceneManager import com.zhjt.mogo_core_function_devatools.status.StatusManager @@ -86,6 +88,8 @@ class DevaToolsProvider : IDevaToolsProvider { private val block by lazy { MoGoBlockProviderImpl() } + private val usage by lazy { MoGoCpuUsageProviderImpl() } + @Volatile private var mDockerVersion: String? = null @@ -394,4 +398,6 @@ class DevaToolsProvider : IDevaToolsProvider { override fun logRecord(): IMoGoLogRecordProvider = logRecordProvider override fun block(): IMoGoBlockProvider? = block + + override fun usage(): IMoGoCpuUsageProvider? = usage } \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MainBlockLinkedLog.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MainBlockLinkedLog.kt index eea4d563f0..25a4a9ab01 100644 --- a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MainBlockLinkedLog.kt +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MainBlockLinkedLog.kt @@ -13,6 +13,10 @@ internal class MainBlockLinkedLog { } } + fun recordCpuUsage(extra: Map) { + recordCpuInternal(extra) + } + @ChainLog( linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK, linkCode = ChainConstant.CHAIN_SOURCE_HMI, @@ -20,4 +24,13 @@ internal class MainBlockLinkedLog { paramIndexes = [0] ) private fun recordInternal(extra: Map>) {} + + + @ChainLog( + linkChainLog = ChainConstant.CHAIN_TYPE_ANR_LEAK, + linkCode = ChainConstant.CHAIN_SOURCE_HMI, + nodeAliasCode = ChainConstant.CHAIN_CODE_MAIN_BLOCK, + paramIndexes = [0] + ) + private fun recordCpuInternal(extra: Map) {} } \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MoGoBlockProviderImpl.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MoGoBlockProviderImpl.kt index b9c7c037e2..f5f9ae659f 100644 --- a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MoGoBlockProviderImpl.kt +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/block/MoGoBlockProviderImpl.kt @@ -11,6 +11,7 @@ import com.mogo.eagle.core.block.runtime.config.recorder.IMessageRecorder.OnDump import com.mogo.eagle.core.block.runtime.listener.* import com.mogo.eagle.core.block.runtime.report.* import com.mogo.eagle.core.function.api.devatools.block.* +import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager import java.util.concurrent.TimeUnit.SECONDS internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener { @@ -26,7 +27,7 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener { .multiplier(2.0f) .isDebug(false) .period(5, SECONDS) - .junkRateThreshold(0.8f) + .junkRateThreshold(0.6f) .recorder(null, 500, 500) .build()) hasInit = true @@ -49,6 +50,19 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener { override fun OnDumped(data: Map>) { map["history"] = data[0] ?: emptyList() map["pending"] = data[1] ?: emptyList() + val cpu = CallerDevaToolsManager.usage()?.dump() + if (!cpu.isNullOrEmpty()) { + val mainThreadCpuUsage = cpu.remove("MainThreadCpuUsage") + val processLaunchedTime = cpu.remove("ProcessLaunchedTime") + if (mainThreadCpuUsage != null && processLaunchedTime != null) { + linkedLog.recordCpuUsage(LinkedHashMap().also { + it["MainThread"] = "${ mainThreadCpuUsage * 1.0f * 100 / processLaunchedTime }%" + cpu.entries.sortedByDescending { it.value }.forEach { sorted -> + it[sorted.key] = "${ sorted.value * 1.0f * 100 / processLaunchedTime }%" + } + }) + } + } linkedLog.record(map) } }) @@ -81,7 +95,6 @@ internal class MoGoBlockProviderImpl: IMoGoBlockProvider, IBlockListener { } override fun recorder(): IMessageRecorder { - return BlockDetector.recorder() } } \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/perf/MoGoCpuUsageProviderImpl.kt b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/perf/MoGoCpuUsageProviderImpl.kt new file mode 100644 index 0000000000..20bcee5eb8 --- /dev/null +++ b/core/function-impl/mogo-core-function-devatools/src/main/java/com/zhjt/mogo_core_function_devatools/perf/MoGoCpuUsageProviderImpl.kt @@ -0,0 +1,81 @@ +package com.zhjt.mogo_core_function_devatools.perf + +import android.os.SystemClock +import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock + +internal class MoGoCpuUsageProviderImpl: IMoGoCpuUsageProvider { + + private var processLaunchTime: Long = 0L + + private var mainThreadStartTime : Long = 0L + + private var mainThreadLastTime: Long = 0L + + private var mainThreadCpuUsage: Long = 0L + + private val maxSize = 500 + + private val map by lazy { LinkedHashMap(0, 0.75f, true) } + + private val lock by lazy { ReentrantLock() } + + private val builder by lazy { ThreadLocal() } + + override fun onProcessLaunched(time: Long) { + processLaunchTime = time + } + + override fun startMainThreadTime(time: Long) { + mainThreadStartTime = time + } + + override fun updateMainThreadTime(time: Long) { + if (mainThreadLastTime == 0L) { + mainThreadLastTime = mainThreadStartTime + } + val delta = time - mainThreadLastTime + if (delta > 0) { + mainThreadCpuUsage += delta + } + mainThreadLastTime = time + } + + override fun incrementOtherThreadUsage(group: String, t: Thread, usage: Long) { + var builder = builder.get() + if (builder == null) { + builder = StringBuilder(128) + this.builder.set(builder) + } + if (builder.isNotEmpty()) { + builder.setLength(0) + } + builder.append(group).append("@@").append(t.name) + lock.withLock { + while (map.size > maxSize) { + val toEvict = map.entries.iterator().next() + map.remove(toEvict.key) + } + map[builder.toString()] = map.getOrPut(builder.toString()) { 0 } + usage + } + } + + override fun dump(): LinkedHashMap { + return lock.withLock { + if (map.isNotEmpty()) { + val iterator = map.entries.iterator() + val rst = LinkedHashMap() + rst["MainThreadCpuUsage"] = mainThreadCpuUsage + rst["ProcessLaunchedTime"] = SystemClock.elapsedRealtimeNanos() - processLaunchTime + while (iterator.hasNext()) { + val next = iterator.next() + rst[next.key] = next.value + } + rst + } else { + LinkedHashMap() + } + } + } +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/threadopt/ThreadOptInitializer.java b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/threadopt/ThreadOptInitializer.java index 65aa172428..645feaa97a 100644 --- a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/threadopt/ThreadOptInitializer.java +++ b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/main/threadopt/ThreadOptInitializer.java @@ -1,39 +1,72 @@ package com.mogo.eagle.core.function.main.threadopt; - +import android.os.Debug; +import android.os.Looper; +import android.os.SystemClock; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.os.HandlerCompat; +import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider; +import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager; import com.mogo.thread.opt.core.ThreadManager; import com.mogo.thread.opt.core.annotation.Keep; import com.mogo.thread.opt.core.config.ThreadConfig; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; @Keep public class ThreadOptInitializer { + public static final AtomicBoolean aRouterInit = new AtomicBoolean(false); + private static final ThreadConfig.TaskExecuteListener listener = new ThreadConfig.TaskExecuteListener() { + + + private volatile boolean recorded = false; + + private final ThreadLocal start = new ThreadLocal<>(); + + @Override public boolean isEnabled() { return true; } + @Override public void onExecutorBefore(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) { - + if (!recorded && aRouterInit.get()) { + IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage(); + if (usage != null) { + recorded = true; + HandlerCompat.createAsync(Looper.getMainLooper()).post(() -> usage.startMainThreadTime(Debug.threadCpuTimeNanos())); + usage.onProcessLaunched(SystemClock.elapsedRealtimeNanos()); + } + } + if (recorded) { + start.set(Debug.threadCpuTimeNanos()); + } } @Override public void onExecutorAfter(@NonNull ThreadConfig.TaskType taskType, @Nullable Object task, @Nullable String desc) { - + if (recorded) { + Long start = this.start.get(); + if (start != null && start > 0) { + long delta = Debug.threadCpuTimeNanos() - start; + IMoGoCpuUsageProvider usage = CallerDevaToolsManager.INSTANCE.usage(); + if (usage != null && desc != null) { + usage.incrementOtherThreadUsage(desc, Thread.currentThread(), delta); + } + } + } } @Override - public void onExecutorStateChanged(@NonNull ThreadPoolExecutor threadPoolExecutor, int core, int max, int active, long completed) { - - } + public void onExecutorStateChanged(@NonNull ThreadPoolExecutor threadPoolExecutor, int core, int max, int active, long completed) {} }; diff --git a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt index b9c91a80bb..750ae690c2 100644 --- a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt +++ b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/IDevaToolsProvider.kt @@ -17,6 +17,7 @@ import com.mogo.eagle.core.function.api.devatools.strict.* import com.mogo.eagle.core.function.api.devatools.download.* import com.mogo.eagle.core.function.api.devatools.logcat.* import com.mogo.eagle.core.function.api.devatools.mofang.* +import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider import com.mogo.eagle.core.function.api.lookaround.* import com.mogo.eagle.core.function.api.upgrade.* @@ -238,4 +239,9 @@ interface IDevaToolsProvider : IProvider { * 主线程卡顿监控 */ fun block(): IMoGoBlockProvider? + + /** + * 各线程CPU使用率 + */ + fun usage(): IMoGoCpuUsageProvider? } \ No newline at end of file diff --git a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/perf/IMoGoCpuUsageProvider.kt b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/perf/IMoGoCpuUsageProvider.kt new file mode 100644 index 0000000000..3de0c9ecac --- /dev/null +++ b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/devatools/perf/IMoGoCpuUsageProvider.kt @@ -0,0 +1,14 @@ +package com.mogo.eagle.core.function.api.devatools.perf + +interface IMoGoCpuUsageProvider { + + fun onProcessLaunched(time: Long) + + fun startMainThreadTime(time: Long) + + fun updateMainThreadTime(time: Long) + + fun incrementOtherThreadUsage(group: String, t: Thread, usage: Long) + + fun dump(): LinkedHashMap +} \ No newline at end of file diff --git a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt index f77633cd9a..f8accbd915 100644 --- a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt +++ b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/devatools/CallerDevaToolsManager.kt @@ -18,6 +18,7 @@ import com.mogo.eagle.core.function.api.devatools.block.* import com.mogo.eagle.core.function.api.devatools.download.* import com.mogo.eagle.core.function.api.devatools.logcat.* import com.mogo.eagle.core.function.api.devatools.mofang.* +import com.mogo.eagle.core.function.api.devatools.perf.IMoGoCpuUsageProvider import com.mogo.eagle.core.function.api.upgrade.* import com.mogo.eagle.core.function.api.devatools.strict.* import com.mogo.eagle.core.function.api.lookaround.* @@ -281,4 +282,6 @@ object CallerDevaToolsManager { fun logcat(): IMoGoLogRecordProvider? = devaToolsProviderApi?.logRecord() fun block(): IMoGoBlockProvider? = devaToolsProviderApi?.block() + + fun usage(): IMoGoCpuUsageProvider? = devaToolsProviderApi?.usage() } \ No newline at end of file diff --git a/gradle/bytex/bytex_lancetx.gradle b/gradle/bytex/bytex_lancetx.gradle index 5758452500..04374719cc 100644 --- a/gradle/bytex/bytex_lancetx.gradle +++ b/gradle/bytex/bytex_lancetx.gradle @@ -3,7 +3,10 @@ LancetX { enable true enableInDebug true synchronizedLock { - enabled false + enabled true + blackList = [ + "okio.AsyncTimeout\$Watchdog" + ] } weaveGroup { @@ -20,7 +23,7 @@ LancetX { enable true } main_block_check { - enable false + enable true } } }