你的debug包在Android 14变卡了吗 | 得物技术
目录
一、背景
二、问题排查纪录
1. 常规手段排查
2. 发现怀疑点
3. 排查怀疑点
4. 排查native耗时
5. 定位到DEBUG_JAVA_DEBUGGABLE
6. 定位到DeoptimizeBootImage
7. 原因分析
8. 验证是系统问题
9. 反馈问题
三、临时解决
四、最后
一
背景
二
问题排查纪录
常规手段排查
发现怀疑点
流程图
The C++ interpreter,也就是传统的基于switch结构的解释器,一般仅在调试环境、方法跟踪、指令不支持或者在字节码发生异常情况下(例如failed structured-locking verification)才走该分支。 The mterp fast interpreter,核心是引入了handler table做指令映射,并通过手写汇编以实现指令间的快速切换,提高了解释器性能。 Nterp是Mterp的再次优化。Nterp省去了managed code stacks的维护,采用了和Native方法一样的栈帧结构,并且译码和翻译执行全程都由汇编代码实现,进一步拉进解释器和compiled code的性能差距。
排查怀疑点
void Runtime::SetRuntimeDebugState(RuntimeDebugState state) {if (state != RuntimeDebugState::kJavaDebuggableAtInit) {// We never change the state if we started as a debuggable runtime.DCHECK(runtime_debug_state_ != RuntimeDebugState::kJavaDebuggableAtInit);}runtime_debug_state_ = state;}
排查native耗时
定位到
DEBUG_JAVA_DEBUGGABLE
那就想着从debuggable的源头入手,逐步缩小范围定位影响变量。
AndroidManifest中的debuggable影响系统system进程启动我们进程中的一个runtimeFlags。
frameworks/base/core/java/android/os/Process.java 中的start方法 其中第6个参数就是runtimeFlags而如果是debuggableFlag runtimeFlags会被添加以下一些flag 那就先缩小标签范围。
if (debuggableFlag) {runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;runtimeFlags |= Zygote.DEBUG_ENABLE_PTRACE;runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;// Also turn on CheckJNI for debuggable apps. It's quite// awkward to turn on otherwise.runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;// Check if the developer does not want ART verificationif (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(),android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) {runtimeFlags |= Zygote.DISABLE_VERIFIER;Slog.w(TAG_PROCESSES, app + ": ART verification disabled");}}
需要修改我们进程的启动参数。那就需要去hook system进程了。这边涉及到手机root,安装hook框架的一些操作,然后通过hook Process的start去做一些参数修改。
hookAllMethods(Process.class,"start",new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {final String niceName = (String) param.args[1];final int uid = (int) param.args[2];final int runtimeFlags = (int) param.args[5];XposedBridge.log("process_xx " + runtimeFlags);if (isDebuggable(niceName, user)) {param.args[5] = runtimeFlags&~DEBUG_JAVA_DEBUGGABLE;XposedBridge.log("process_xx " + param.args[5]);}}});
这次还是有一些明显的结果的。测试包 runtimeflags 移除DEBUG_JAVA_DEBUGGABLE后不卡了。而生产包包括应用市场上的应用加上DEBUG_JAVA_DEBUGGABLE标记后全部都变卡了。那就可以证明是DEBUG_JAVA_DEBUGGABLE这个变量引起的。
定位到
DeoptimizeBootImage
继续源码观察DEBUG_JAVA_DEBUGGABLE带来的影响。
if ((runtime_flags & DEBUG_JAVA_DEBUGGABLE) != 0) {runtime->AddCompilerOption("--debuggable");runtime_flags |= DEBUG_GENERATE_MINI_DEBUG_INFO;runtime->SetRuntimeDebugState(Runtime::RuntimeDebugState::kJavaDebuggableAtInit);{// Deoptimize the boot image as it may be non-debuggable.ScopedSuspendAll ssa(__FUNCTION__);runtime->DeoptimizeBootImage();}runtime_flags &= ~DEBUG_JAVA_DEBUGGABLE;needs_non_debuggable_classes = true;}
这里有逻辑是DEBUG_JAVA_DEBUGGABLE带来的影响点,SetRuntimeDebugState之前已经测试过了。也不是DEBUG_GENERATE_MINI_DEBUG_INFO带来的影响,那是runtime->DeoptimizeBootImage()?于是我用debugable为false的包通过_ZN3art7Runtime19DeoptimizeBootImageEv主动去调用了DeoptimizeBootImage方法,然后复现了!
原因分析
验证是系统问题
反馈问题
三
临时解决
void Instrumentation::UpdateEntrypointsForDebuggable() {Runtime* runtime = Runtime::Current();// If we are transitioning from non-debuggable to debuggable, we patch// entry points of methods to remove any aot / JITed entry points.InstallStubsClassVisitor visitor(this);runtime->GetClassLinker()->VisitClasses(&visitor);}
Java_test_ArtMethodTrace_bootImageNterp(JNIEnv *env,jclass clazz) {void *handler = shadowhook_dlopen("libart.so");instance_ = static_cast<void **>(shadowhook_dlsym(handler, "_ZN3art7Runtime9instance_E"));jobject(*getSystemThreadGroup)(void *runtime) =(jobject (*)(void *runtime)) shadowhook_dlsym(handler,"_ZNK3art7Runtime20GetSystemThreadGroupEv");void(*UpdateEntrypointsForDebuggable)(void *instrumentation) = (void (*)(void *i)) shadowhook_dlsym(handler,"_ZN3art15instrumentation15Instrumentation30UpdateEntrypointsForDebuggableEv");if (getSystemThreadGroup == nullptr || UpdateEntrypointsForDebuggable == nullptr) {LOGE("getSystemThreadGroup failed ");shadowhook_dlclose(handler);return;}jobject thread_group = getSystemThreadGroup(*instance_);int vm_offset = findOffset(*instance_, 0, 4000, thread_group);if (vm_offset < 0) {LOGE("vm_offset not found ");shadowhook_dlclose(handler);return;}void (*setRuntimeDebugState)(void *instance_, int r) =(void (*)(void *runtime,int r)) shadowhook_dlsym(handler, "_ZN3art7Runtime20SetRuntimeDebugStateENS0_17RuntimeDebugStateE");if (setRuntimeDebugState != nullptr) {setRuntimeDebugState(*instance_, 0);}void *instrumentation = reinterpret_cast<void *>(reinterpret_cast<char *>(*instance_) +vm_offset - 368 );UpdateEntrypointsForDebuggable(instrumentation);setRuntimeDebugState(*instance_, 2);shadowhook_dlclose(handler);LOGE("bootImageNterp success");}
四
最后
往期回顾
1. 沙龙回顾|行“稳”致远,“生”生不息!得物稳定生产专场沙龙
2. 基于Java代码模型生成质量平台自动化用例方案与实践 | 得物技术
3. 得物技术海量职位“职”等你来!速投!
4. 得物千人规模敏捷迭代实践分享
文 / 乌柚
关注得物技术,每周一、三、五更新技术干货
要是觉得文章对你有帮助的话,欢迎评论转发点赞~
未经得物技术许可严禁转载,否则依法追究法律责任。
“
扫码添加小助手微信
如有任何疑问,或想要了解更多技术资讯,请添加小助手微信:
线下活动推荐
主题:无线优化技术沙龙
时间:2024年5月26日
地点:上海杨浦区黄兴路互联·宝地C2栋5层/线上同步直播
参与方式:请持续关注「得物技术」公众号,沙龙详细介绍及报名方式将会在公众号推送