初阶技能:Android 应用异常如何丰富线索

在我们容器虚拟化产品开发过程中,时常会遇到某些应用无法启动或运行时异常崩溃的问题;让应用行为信息丰富,则能还原应用异常发生过程,对我们快速分析问题至关重要。

在这里需要用到以下几个开源工具:

01. 举个栗子

我们在做应用容器时,尝尝会遇到应用进程崩溃,通过adb抓取的日志,却只有很少的几行日志,没有其他任何信息,因此通过一些简单的方式让应用行为的痕迹还原,是我们第一步要做的事情。

tex 复制代码
9402 9402 D AndroidRuntime: Shutting down VM  
9402 9402 I Process : Sending signal. PID: 9402 SIG: 9  

02. 打印退出痕迹

通过拦截native函数,打印Java层的异常退出:

java 复制代码
import android.os;  
  
public class Process {  
  
/**  
* Returns the identifier of this process, which can be used with  
* {@link #killProcess} and {@link #sendSignal}.  
*/  
public static final native void sendSignal(int pid, int signal);  
  
/**  
* @hide  
* Private impl for avoiding a log message... DO NOT USE without doing  
* your own log, or the Android Illuminati will find you some night and  
* beat you up.  
*/  
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)  
public static final native void sendSignalQuiet(int pid, int signal);  
  
}  
  
public class Runtime {  
private static native void nativeExit(int code);  
}  

通过拦截以下函数,打印Native层的异常退出:

c 复制代码
void exit(int status);  
int kill(pid_t pid, int sig);  

打印调用栈:

c 复制代码
// 非 JNI 环境获取从当前线程获取 JNIEnv(AttachCurrentThread)  
void jni_thread_dump(JNIEnv* env) {  
jclass jcls = env->FindClass("java/lang/Thread");  
if (!jcls) return;  
jmethodID jm = env->GetStaticMethodID(jcls, "dumpStack", "()V");  
if (!jm) return;  
env->CallStaticVoidMethod(jcls, jm);  
env->ExceptionClear();  
}  

输出结果:

java 复制代码
5511 5511 W System.err: java.lang.Exception: Stack trace  
5511 5511 W System.err: at java.lang.Thread.dumpStack(Thread.java:1527)  
5511 5511 W System.err: at android.os.Process.sendSignal(Native Method)  
5511 5511 W System.err: at android.os.Process.killProcess(Process.java:1295)  
5511 5511 W System.err: at com.android.internal.os.RuntimeInit$KillApplicationHandler.uncaughtException(RuntimeInit.java:207)  
5511 5511 W System.err: at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)  
5511 5511 W System.err: at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)  
5511 5511 W System.err: at java.lang.Thread.dispatchUncaughtException(Thread.java:2211)  

03. 追踪Throwable异常调用栈

在我们常用的try ... catch语句中,异常时打印调用栈是常规操作,在这里可以拦截并打印栈日志信息。

java 复制代码
import java.lang;  
  
public class Throwable implements Serializable {  
  
private static native StackTraceElement[] nativeGetStackTrace(Object stackState);  
  
private static native Object nativeFillInStackTrace();  
  
}  

04. 打印VM异常调用栈

java 复制代码
package dalvik.system;  
  
/**  
* Provides a limited interface to the Dalvik VM stack. This class is mostly  
* used for implementing security checks.  
*/  
public final class VMStack {  
/**  
* Retrieves the stack trace from the specified thread.  
*  
* @param t  
* thread of interest  
* @return an array of stack trace elements, or null if the thread  
* doesn't have a stack trace (e.g. because it exited)  
*/  
native public static StackTraceElement[] getThreadStackTrace(Thread t);  
  
/**  
* Retrieves an annotated stack trace from the specified thread.  
*  
* @param t  
* thread of interest  
* @return an array of annotated stack frames, or null if the thread  
* doesn't have a stack trace (e.g. because it exited)  
*/  
native public static AnnotatedStackTraceElement[] getAnnotatedThreadStackTrace(Thread t);  
  
/**  
* Retrieves a partial stack trace from the specified thread into  
* the provided array.  
*  
* @param t  
* thread of interest  
* @param stackTraceElements  
* preallocated array for use when only the top of stack is  
* desired. Unused elements will be filled with null values.  
* @return the number of elements filled  
*/  
native public static int fillStackTraceElements(Thread t, StackTraceElement[] stackTraceElements);  
}  

输出:

tex 复制代码
9402 9402 W stack : android.app.LoadedApk.makeApplication(LoadedApk.java:1554)  
9402 9402 W stack : android.app.ActivityThread.handleBindApplication(ActivityThread.java:8522)  
9402 9402 W stack : android.app.ActivityThread.access$2800(ActivityThread.java:311)  
9402 9402 W stack : android.app.ActivityThread$H.handleMessage(ActivityThread.java:2889)  
9402 9402 W stack : android.os.Handler.dispatchMessage(Handler.java:117)  
9402 9402 W stack : android.os.Looper.loopOnce(Looper.java:205)  
9402 9402 W stack : android.os.Looper.loop(Looper.java:293)  
9402 9402 W stack : android.app.ActivityThread.loopProcess(ActivityThread.java:9934)  
9402 9402 W stack : android.app.ActivityThread.main(ActivityThread.java:9923)  
9402 9402 W stack : java.lang.reflect.Method.invoke(Native Method)  
9402 9402 W stack : com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)  
9402 9402 W stack : com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)  
9402 9402 W stack : ohos.abilityshell.HarmonyLoader.tryLoadHarmony(HarmonyLoader.java:130)  
9402 9402 W stack : ohos.abilityshell.HarmonyApplication.tryLoadHarmony(HarmonyApplication.java:673)  
9402 9402 W stack : ohos.abilityshell.HarmonyApplication.attachBaseContext(HarmonyApplication.java:168)  
9402 9402 W stack : com.demo.app.DemoApp.attachBaseContext(SourceFile:1)  
9402 9402 W stack : android.app.Application.attach(Application.java:338)  
9402 9402 W stack : android.app.Instrumentation.newApplication(Instrumentation.java:1191)  
9402 9402 W stack : android.app.LoadedApk.makeApplication(LoadedApk.java:1546)  
9402 9402 W stack : android.app.ActivityThread.handleBindApplication(ActivityThread.java:8522)  
9402 9402 W stack : android.app.ActivityThread.access$2800(ActivityThread.java:311)  
9402 9402 W stack : android.app.ActivityThread$H.handleMessage(ActivityThread.java:2889)  
9402 9402 W stack : android.os.Handler.dispatchMessage(Handler.java:117)  
9402 9402 W stack : android.os.Looper.loopOnce(Looper.java:205)  
9402 9402 W stack : android.os.Looper.loop(Looper.java:293)  
9402 9402 W stack : android.app.ActivityThread.loopProcess(ActivityThread.java:9934)  
9402 9402 W stack : android.app.ActivityThread.main(ActivityThread.java:9923)  
9402 9402 W stack : java.lang.reflect.Method.invoke(Native Method)  
9402 9402 W stack : com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)  
9402 9402 W stack : com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)  

05. 打印异常信息

Android框架默认会为每个应用进程设置一个全局的异常Handler,我们可以替换掉输出打印更多内容。在这里要考虑应用自身也会设置的情况。

java 复制代码
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {  
@Override  
public void uncaughtException(Thread t, Throwable e) {  
// TODO  
}  
});  

06. 打印异常传送

AndroidAMS服务中增加了一个应用向服务提交异常的接口,我们通过android.reflect.Proxy方式代理IActivityManager的实例,打印输出。

java 复制代码
package android.app;  
  
// @source code: /frameworks/base/core/java/android/app/IActivityManager.aidl  
interface IActivityManager {  
void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo);  
}  
  
// @source code: /frameworks/base/core/java/android/app/ApplicationErrorReport.java  
public class ApplicationErrorReport implements Parcelable {  
public static class CrashInfo {  
/**  
* Dump a CrashInfo instance to a Printer.  
*/  
public void dump(Printer pw, String prefix) {  
pw.println(prefix + "exceptionHandlerClassName: " + exceptionHandlerClassName);  
pw.println(prefix + "exceptionClassName: " + exceptionClassName);  
pw.println(prefix + "exceptionMessage: " + exceptionMessage);  
pw.println(prefix + "throwFileName: " + throwFileName);  
pw.println(prefix + "throwClassName: " + throwClassName);  
pw.println(prefix + "throwMethodName: " + throwMethodName);  
pw.println(prefix + "throwLineNumber: " + throwLineNumber);  
pw.println(prefix + "stackTrace: " + stackTrace);  
}  
}  
public static class ParcelableCrashInfo extends CrashInfo implements Parcelable {  
  
}  
}  

如输出结果为:

tex 复制代码
9402 9402 W Crash : exceptionClassName: java.lang.IllegalStateException  
9402 9402 W Crash : exceptionMessage: failed to attach Application, errorCode=30, errorInfo=bms service error, code is 8519969. 8519797  
9402 9402 W Crash : throwFileName: HarmonyLoader.java  
9402 9402 W Crash : throwClassName: ohos.abilityshell.HarmonyLoader  
9402 9402 W Crash : throwMethodName: tryLoadHarmony  
9402 9402 W Crash : throwLineNumber: 130  
9402 9402 W Crash : stackTrace: java.lang.RuntimeException: Unable to instantiate application com.demo.app.DemoApp package com.demo.app: java.lang.IllegalStateException: failed to attach Application, errorCode=30, errorInfo=bms service error, code is 8519969.  
9402 9402 W Crash : at android.app.LoadedApk.makeApplication(LoadedApk.java:1554)  
9402 9402 W Crash : at android.app.ActivityThread.handleBindApplication(ActivityThread.java:8522)  
9402 9402 W Crash : at android.app.ActivityThread.access$2800(ActivityThread.java:311)  
9402 9402 W Crash : at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2889)  
9402 9402 W Crash : at android.os.Handler.dispatchMessage(Handler.java:117)  
9402 9402 W Crash : at android.os.Looper.loopOnce(Looper.java:205)  
9402 9402 W Crash : at android.os.Looper.loop(Looper.java:293)  
9402 9402 W Crash : at android.app.ActivityThread.loopProcess(ActivityThread.java:9934)  
9402 9402 W Crash : at android.app.ActivityThread.main(ActivityThread.java:9923)  
9402 9402 W Crash : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)  
9402 9402 W Crash : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)  
9402 9402 W Crash : Caused by: java.lang.IllegalStateException: fail  

07. 打印Binder通信信息

可以打印应用访问了那些系统服务。可以通过Binderceptor项目来完成。

tex 复制代码
9402 9402 W Trace : Binder::tgt: 0x3887b52, 3, : android.content.pm.IPackageManager  
9402 9402 W Trace : Binder::tgt: 0xe45f2c2, 2, : android.os.IServiceManager  
9402 9402 W Trace : Binder::tgt: 0x76c2822, 56, : com.huawei.android.view.IHwWindowManager  
9402 9417 W Trace : Binder::tgt: 0x3887b52, 20, : android.content.pm.IPackageManager  
9402 9402 W Trace : Binder::tgt: 0x7db76a2, 30, : android.net.IConnectivityManager  
9402 9402 W Trace : Binder::tgt: 0x3887b52, 20, : android.content.pm.IPackageManager  
9402 9402 W Trace : Binder::tgt: 0x3887b52, 9, : android.content.pm.IPackageManager  
9402 9402 W Trace : Binder::tgt: 0x3887b52, 95, : android.content.pm.IPackageManager  
9402 9402 W Trace : Binder::tgt: 0x3887b52, 3, : android.content.pm.IPackageManager  
......  

binder codeaidl接口函数的对应关系:

java 复制代码
public static void printStub(String clsName) {  
android.util.Log.w(TAG, ">>>>>> " + clsName);  
try {  
Class<?> nmClassStub = Class.findClass(clsName + "$Stub");  
Method[] mm = nmClassStub.getMethods();  
  
ArrayList<TRANSACTION_Item> list = new ArrayList<TRANSACTION_Item>();  
for (Method m : mm) {  
if (TextUtils.equals(m.getName(), "asBinder")) continue;  
try {  
funcName = m.toGenericString();  
funcName = funcName.replace("abstract ", "");  
funcName = funcName.replace(clsName + ".", "");  
  
fieldName = "TRANSACTION_" + m.getName();  
Field f = nmClassStub.getDeclaredField(fieldName);  
f.setAccessible(true);  
int val = f.getInt(null);  
// android.util.Log.w(TAG, fieldName + " " + val);  
list.add(new TRANSACTION_Item(val, funcName, fieldName));  
} catch (Exception e) {  
android.util.Log.e(TAG, e.toString());  
}  
}  
Collections.sort(list, new TRANSACTION_Comparator());  
} catch (Exception e) {  
android.util.Log.e(TAG, e.toString());  
}  
android.util.Log.w(TAG, "<<<<<< " + clsName);  
}  

排序后可以得到输出的结果:

java 复制代码
package android.content.pm;  
  
interface IPackageManager {  
public void checkPackageStartable(java.lang.String,int) throws android.os.RemoteException;// 1  
public boolean isPackageAvailable(java.lang.String,int) throws android.os.RemoteException;// 2  
public android.content.pm.PackageInfo getPackageInfo(java.lang.String,int,int) throws android.os.RemoteException;// 3  
public android.content.pm.PackageInfo getPackageInfoVersioned(android.content.pm.VersionedPackage,int,int) throws android.os.RemoteException;// 4  
}  

08. 打印JNI调用轨迹

通过拦截JNIEnv层提供调用Java层方法的函数,打印调用信息。

c++ 复制代码
env->FindClass(...);  
env->GetMethodID(...);  
env->GetStaticMethodID(...);  
env->RegisterNatives(...);  

09. 打印访问文件行为

通过拦截open系列函数(为什么是系列,因为libc.so经过多年迭代,衍生出来很多函数,所以说Android的兼容性让广大开发者开发Demo容易,做稳定则难上加难),打印文件访问记录。

c 复制代码
int open(const char *fileName, int flags, ...);  
int open2(const char *fileName, int flags, ...);  
int _open(const char *fileName, int flags, ...);  
int openat(int dirfd, const char *fileName, int flags, ...);  
int _openat(int dirfd, const char *fileName, int flags, ...);  
...  

至此,通过以上基本的方法,可以快速的增加应用运行轨迹信息。

若还不够,则需要针对应用内部调试,动态库加载的进阶方法深一步剖析,后面会给大家整理进阶技能。

相关推荐
冰 河4 小时前
《Nginx核心技术》第16章:实现Nginx的高可用负载均衡
运维·nginx·程序员·负载均衡·高可用
Android技术栈3 天前
鸿蒙(API 12 Beta6版)图形【 请求动画绘制帧率】方舟2D图形服务
程序员·harmonyos·鸿蒙·鸿蒙系统·openharmony·方舟2d图形·动画绘制
程序员鱼皮6 天前
大厂为啥都发苹果电脑?哪个系统是开发之王?
计算机·程序员·互联网·开发·编程经验
Android技术栈6 天前
鸿蒙(API 12 Beta3版)【通过字节数组生成码图】
程序员·移动开发·harmonyos·鸿蒙·鸿蒙系统·openharmony
数造科技6 天前
数造科技荣登“科创杯”领奖台,开启数据驱动新篇章!
大数据·运维·科技·创业创新·敏捷开发
Android技术栈8 天前
鸿蒙(API 12 Beta5版)【通过文本生成码图】
程序员·移动开发·harmonyos·鸿蒙·鸿蒙系统·openharmony·扫码
一丝晨光9 天前
你真的理解编程语言里的数据相等吗
java·开发语言·c++·面试·程序员·编程·相等
Android技术栈11 天前
鸿蒙(API 12 Beta3版)【使用ImageEffect编辑图片】图片开发指导
程序员·harmonyos·鸿蒙·鸿蒙系统·媒体·openharmony·图片
Android技术栈13 天前
鸿蒙(API 12 Beta3版)【使用智能PhotoPicker】Media Library Kit媒体文件管理服务
程序员·音视频·harmonyos·鸿蒙·鸿蒙系统·openharmony
程序员鱼皮14 天前
每天那么多工作,我为什么能做到 "不忘事" ?
计算机·程序员·互联网·项目