提交 b51c5a73 编写于 作者: N neighbWang 提交者: Johnson Lee

FinalizerWatchdogDaemon optimization

上级 f192f75a
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly project(':booster-android-api')
compile project(':booster-android-instrument')
}
package com.didiglobal.booster.instrument;
import android.os.Looper;
import android.util.Log;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeoutException;
import static com.didiglobal.booster.android.bugfix.Constants.TAG;
/**
* Stop thread {@code FinalizerWatchdogDaemon} to prevent crash caused by {@link TimeoutException}
*
* @author neighbWang
*/
public class FinalizerWatchdogDaemonKiller {
private static final int MAX_RETRY_TIMES = 10;
private static final long THREAD_SLEEP_TIME = 5000;
@SuppressWarnings("unchecked")
public static void kill() {
new Thread(new Runnable() {
@Override
public void run() {
for (int retry = 0; isFinalizerWatchdogDaemonExists() && retry < MAX_RETRY_TIMES; retry++) {
try {
final Class clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");
final Field field = clazz.getDeclaredField("INSTANCE");
field.setAccessible(true);
final Object watchdog = field.get(null);
try {
final Field thread = clazz.getSuperclass().getDeclaredField("thread");
thread.setAccessible(true);
thread.set(watchdog, null);
} catch (final Throwable t) {
Log.e(TAG, "Clearing reference of thread `FinalizerWatchdogDaemon` failed", t);
try {
final Method method = clazz.getSuperclass().getDeclaredMethod("stop");
method.setAccessible(true);
method.invoke(watchdog);
} catch (final Throwable e) {
Log.e(TAG, "Interrupting thread `FinalizerWatchdogDaemon` failed", e);
break;
}
}
try {
Thread.sleep(THREAD_SLEEP_TIME);
} catch (final InterruptedException ignore) {
}
} catch (final Throwable t) {
Log.e(TAG, "Killing thread `FinalizerWatchdogDaemon` failed", t);
break;
}
}
if (isFinalizerWatchdogDaemonExists()) {
Log.e(TAG, "Killing thread `FinalizerWatchdogDaemon` failed");
} else {
Log.i(TAG, "Thread `FinalizerWatchdogDaemon` does not exist");
}
}
}, "FinalizerWatchdogDaemonKiller").start();
}
private static boolean isFinalizerWatchdogDaemonExists() {
try {
for (final Thread t : getAllThreads()) {
if (null != t && "FinalizerWatchdogDaemon".equals(t.getName())) {
return true;
}
}
} catch (final Throwable ignore) {
}
return false;
}
private static Collection<Thread> getAllThreads() {
try {
final ThreadGroup root = Looper.getMainLooper().getThread().getThreadGroup().getParent();
final Thread[] src = new Thread[root.activeCount()];
final int n = root.enumerate(src);
if (n != src.length) {
final Thread[] target = new Thread[n];
System.arraycopy(src, 0, target, 0, n);
return Arrays.asList(target);
} else {
return Arrays.asList(src);
}
} catch (final Throwable t) {
return Thread.getAllStackTraces().keySet();
}
}
}
......@@ -8,4 +8,5 @@ dependencies {
compile project(':booster-transform-media-player')
compile project(':booster-transform-webview')
compile project(':booster-transform-res-check')
compile project(':booster-transform-finalizer-watchdog-daemon')
}
apply from: '../gradle/booster.gradle'
dependencies {
kapt "com.google.auto.service:auto-service:1.0-rc4"
implementation project(':booster-android-gradle-api')
implementation project(':booster-task-spi')
implementation project(':booster-transform-asm')
compileOnly 'com.android.tools.build:gradle:3.0.0'
}
package com.didiglobal.booster.transform.finalizer.watchdog.daemon
import com.didiglobal.booster.kotlinx.file
import com.didiglobal.booster.kotlinx.touch
import com.didiglobal.booster.transform.ArtifactManager
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.asm.className
import com.didiglobal.booster.transform.asm.findAll
import com.didiglobal.booster.util.ComponentHandler
import com.google.auto.service.AutoService
import org.objectweb.asm.Opcodes.ACC_PROTECTED
import org.objectweb.asm.Opcodes.ALOAD
import org.objectweb.asm.Opcodes.ATHROW
import org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.objectweb.asm.Opcodes.INVOKESTATIC
import org.objectweb.asm.Opcodes.RETURN
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.tree.VarInsnNode
import java.io.PrintWriter
import javax.xml.parsers.SAXParserFactory
/**
* Represents a class transformer for `FinalizerWatchdogDaemon` Thread
* @author neighbWang
*/
@AutoService(ClassTransformer::class)
class FinalizerWatchdogDaemonTransformer : ClassTransformer {
private lateinit var logger: PrintWriter
private val applications = mutableSetOf<String>()
override fun onPreTransform(context: TransformContext) {
val parser = SAXParserFactory.newInstance().newSAXParser()
context.artifacts.get(ArtifactManager.MERGED_MANIFESTS).forEach { manifest ->
val handler = ComponentHandler()
parser.parse(manifest, handler)
applications.addAll(handler.applications)
}
this.logger = context.reportsDir.file(Build.ARTIFACT).file(context.name).file("report.txt").touch().printWriter()
}
override fun onPostTransform(context: TransformContext) {
this.logger.close()
}
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
if (!this.applications.contains(klass.className)) {
return klass
}
val method = klass.methods?.find {
"${it.name}${it.desc}" == "attachBaseContext(Landroid/content/Context;)V"
} ?: klass.defaultAttachBaseContext
method.instructions?.findAll(RETURN, ATHROW)?.forEach {
method.instructions?.insertBefore(it, MethodInsnNode(INVOKESTATIC, FINALIZER_WATCHDOG_DAEMON_KILLER, "kill", "()V", false))
logger.println(" + $FINALIZER_WATCHDOG_DAEMON_KILLER.kill()V before @${if (it.opcode == ATHROW) "athrow" else "return"}: ${klass.name}.${method.name}${method.desc} ")
}
return klass
}
}
private val ClassNode.defaultAttachBaseContext
get() = MethodNode(ACC_PROTECTED, "attachBaseContext", "(Landroid/content/Context;)V", null, null).apply {
instructions.insert(InsnList().apply {
add(VarInsnNode(ALOAD, 0))
add(VarInsnNode(ALOAD, 1))
add(MethodInsnNode(INVOKESPECIAL, superName, name, desc, false))
add(InsnNode(RETURN))
})
maxStack = 1
methods?.add(this)
}
private const val FINALIZER_WATCHDOG_DAEMON_KILLER = "com/didiglobal/booster/instrument/FinalizerWatchdogDaemonKiller"
package com.didiglobal.booster.transform.finalizer.watchdog.daemon
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.LibraryVariant
import com.didiglobal.booster.gradle.scope
import com.didiglobal.booster.task.spi.VariantProcessor
import com.google.auto.service.AutoService
/**
* @author neighbWang
*/
@AutoService(VariantProcessor::class)
class FinalizerWatchdogDaemonVariantProcessor : VariantProcessor {
override fun process(variant: BaseVariant) {
if (variant !is LibraryVariant) {
variant.scope.globalScope.project.dependencies.add("implementation", "${Build.GROUP}:booster-android-instrument-finalizer-watchdog-daemon:${Build.VERSION}")
}
}
}
\ No newline at end of file
include ':booster-aapt2'
include ':booster-android-api'
include ':booster-android-instrument'
include ':booster-android-instrument-finalizer-watchdog-daemon'
include ':booster-android-instrument-media-player'
include ':booster-android-instrument-res-check'
include ':booster-android-instrument-shared-preferences'
include ':booster-android-instrument-thread'
include ':booster-android-instrument-toast'
include ':booster-android-instrument-webview'
include ':booster-android-instrument-res-check'
include ':booster-android-gradle-api'
include ':booster-android-gradle-v3_0'
include ':booster-android-gradle-v3_2'
......@@ -20,16 +21,17 @@ include ':booster-task-permission'
include ':booster-task-spi'
include ':booster-transform-all'
include ':booster-transform-asm'
include ':booster-transform-finalizer-watchdog-daemon'
include ':booster-transform-lint'
include ':booster-transform-spi'
include ':booster-transform-media-player'
include ':booster-transform-res-check'
include ':booster-transform-shared-preferences'
include ':booster-transform-shrink'
include ':booster-transform-spi'
include ':booster-transform-thread'
include ':booster-transform-toast'
include ':booster-transform-usage'
include ':booster-transform-util'
include ':booster-kotlinx'
include ':booster-transform-webview'
include ':booster-transform-res-check'
include ':booster-kotlinx'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册