[6.2.0][技术优化] 打开监测开关

This commit is contained in:
renwj
2023-11-15 16:48:20 +08:00
parent 9edc1045c4
commit 1dcdac0015
15 changed files with 622 additions and 292 deletions

View File

@@ -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<Int, WeakReference<Holder>>(128)
private val handlerTokens by lazy { ConcurrentHashMap<String, CopyOnWriteArrayList<Holder>>() }
private val pool by lazy { Executors.newFixedThreadPool(2) }
private val pool by lazy { Pools.SimplePool<ActionWrapper>(50) }
val asyncHandlers by lazy { CopyOnWriteArraySet<WeakReference<Handler>>() }
private val handlersMap by lazy { ConcurrentHashMap<String, Boolean>() }
private val whats by lazy { ConcurrentHashMap<Int, LinkedList<WeakReference<Holder>>>() }
private val actionPool by lazy { Pools.SimplePool<PoolAction>(500) }
private val actions by lazy { ConcurrentHashMap<Runnable, ActionWrapper>() }
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<WeakReference<Handler>>() }
override fun run() {
try {
val h = holder
if (type == 0 && h != null) {
whats.getOrPut(what) { LinkedList<WeakReference<Holder>>() }.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<Int, Boolean>() }
fun reset() {
type = -1
what = Int.MIN_VALUE
holder = null
elapsedTime = -1
duration = -1
private val handlerWhats by lazy { ConcurrentHashMap<Int, CopyOnWriteArraySet<Int>>() }
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<Holder>(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<Holder>(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

View File

@@ -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;
}

View File

@@ -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<StringBuilder> message = new ThreadLocal<>();
private final ThreadLocal<Object> monitor = new ThreadLocal<>();
private final AtomicReference<Thread> 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) {}
}

View File

@@ -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缓存路由表如果出现了被删除的情况会报错这里清除下就好了

View File

@@ -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'

View File

@@ -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"

View File

@@ -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
}

View File

@@ -13,6 +13,10 @@ internal class MainBlockLinkedLog {
}
}
fun recordCpuUsage(extra: Map<String, String>) {
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<String, List<String>>) {}
@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<String, String>) {}
}

View File

@@ -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<Int, List<String>>) {
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<String, String>().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()
}
}

View File

@@ -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<String, Long>(0, 0.75f, true) }
private val lock by lazy { ReentrantLock() }
private val builder by lazy { ThreadLocal<StringBuilder>() }
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<String, Long> {
return lock.withLock {
if (map.isNotEmpty()) {
val iterator = map.entries.iterator()
val rst = LinkedHashMap<String, Long>()
rst["MainThreadCpuUsage"] = mainThreadCpuUsage
rst["ProcessLaunchedTime"] = SystemClock.elapsedRealtimeNanos() - processLaunchTime
while (iterator.hasNext()) {
val next = iterator.next()
rst[next.key] = next.value
}
rst
} else {
LinkedHashMap()
}
}
}
}

View File

@@ -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<Long> 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) {}
};

View File

@@ -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?
}

View File

@@ -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<String, Long>
}

View File

@@ -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()
}

View File

@@ -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
}
}
}