最近项目Monkey测试的时候A应用总是出现如下问题,导致Crash
06-30 11:43:36.160 23438 23438 E InputChannel-JNI: Error 24 dup channel fd 74.
06-30 11:43:36.161 23438 23438 D AndroidRuntime: Shutting down VM
06-30 11:43:36.161 23438 23438 E AndroidRuntime: FATAL EXCEPTION: main
06-30 11:43:36.161 23438 23438 E AndroidRuntime: Process: ****, PID: 23438
06-30 11:43:36.161 23438 23438 E AndroidRuntime: java.lang.RuntimeException: Could not read input channel file descriptors from parcel.
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.view.InputChannel.nativeReadFromParcel(Native Method)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.view.InputChannel.readFromParcel(InputChannel.java:148)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.view.IWindowSession$Stub$Proxy.addToDisplay(IWindowSession.java:804)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.view.ViewRootImpl.setView(ViewRootImpl.java:773)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.widget.Toast$TN.handleShow(Toast.java:496)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.widget.Toast$TN$1.handleMessage(Toast.java:400)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.os.Looper.loop(Looper.java:164)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6558)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)
06-30 11:43:36.161 23438 23438 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)
06-30 11:43:36.210 1384 5096 I am_crash: [23438,0,****,954908228,java.lang.RuntimeException,Could not read input channel file descriptors from parcel.,InputChannel.java,-2]
遇到这个问题,咋一看Log相当的糊人,貌似很难解决,我们这里就废话不多说了,直接正常的分析问题思路走起。
从Log中来看我们最终是在调用nativeReadFromParcel方法出了问题,直接定位关键代码
static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
jobject parcelObj) {
if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"This object already has a native input channel.");
return;
}
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
String8 name = parcel->readString8();
int rawFd = parcel->readFileDescriptor();
int dupFd = dup(rawFd);
if (dupFd < 0) {
ALOGE("Error %d dup channel fd %d.", errno, rawFd);//此处还会打印错误的原因
jniThrowRuntimeException(env,
"Could not read input channel file descriptors from parcel.");
return;
}//异常就是从这里抛出的。
InputChannel* inputChannel = new InputChannel(name, dupFd);
NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
}
}
}
定位关键Log,06-30 11:43:36.160 23438 23438 E InputChannel-JNI: Error
24 dup channel fd 74. 24就是整个问题出错的原因,我们可以在系统源码中找到它的定义 ERRNO_VALUE(EMFILE, 24);关于EMFILE相关的解释,可以百度一下,具体可以参考一下文章最后的一个博客链接。我们这里EMFILE 24指的就是我们的应用进程fd泄漏,越过上限值。Android中fd大体指的就是那个epoll,IO多路复用技术,直白点就是进程线程间通信的方式。一般创建带有Looper的线程,创建Window的时候会创建fd,Looper的fd就是为什么Android应用进程执行Looper死循环而不卡死主线程的根本原理所在,Window的fd就是InputChannel通信的fd
查看进程FD上限方法:这里以系统system_server进程为例。adb shell进入手机根目录,cd到system_server进程目录下,ls查看一下。
/proc/916 # ls
attr clear_refs cpuset fd make-it-fail mounts oom_adj personality sched_group_id smaps status wchan
autogroup cmdline cwd fdinfo maps mountstats oom_score reclaim sched_init_task_load stack syscall
auxv comm environ io mem net oom_score_adj root sched_wake_up_idle stat task
cgroup coredump_filter exe limits mountinfo ns pagemap sched schedstat statm timerslack_ns
主需要关注fd和limits,先cat limits
/proc/916 # cat limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 21911 21911 processes
Max open files 1024 4096 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 21911 21911 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 40 40
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
实际测试证明这个1024就是系统对当前进程fd打开数量的限制。
cd到fd目录,执行ls -l | wc -l,查看进程创建了多少fd
/proc/916/fd # ls -l | wc -l
372//这里就是当前进程fd创建的数量
我们知道问题可能发生的原因之后,又知道应用创建Window和Looper会创建fd,那我们就得去定位自己的代码了,不断的检查应用的代码,我们发现问题应用存在Activity leak,而泄漏的Activity中又使用了HandlerThread,并且还会不停的Toast.show。最终优化完代码之后,monkey测试问题应用48小时,该问题不再复现。
这里只是介绍了最终是由于Toast.show,系统申请fd失败,应用crash的原因,而实际开发中如果我们不当的使用HandlerThread,或者自定义带有looper的线程时(没有调用Looper.quit),也会造成fd泄漏,具体的错误如下
looper fd泄漏
05-31 07:32:38.634 12175 12175 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
05-31 07:32:38.634 12175 12175 F DEBUG : Build fingerprint: ********************
05-31 07:32:38.634 12175 12175 F DEBUG : Revision: '0'
05-31 07:32:38.634 12175 12175 F DEBUG : ABI: 'arm64'
05-31 07:32:38.635 12175 12175 F DEBUG : pid: 11288, tid: 12172, name: 481 HandlerThre >>> com.example.procfdtest <<<
05-31 07:32:38.635 12175 12175 F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
05-31 07:32:38.648 12175 12175 F DEBUG : Abort message: 'Could not create epoll instance: Too many open files'
05-31 07:32:38.648 12175 12175 F DEBUG : x0 0000000000000000 x1 0000000000002f8c x2 0000000000000006 x3 0000000000000008
05-31 07:32:38.648 12175 12175 F DEBUG : x4 0000000000000000 x5 0000000000000000 x6 0000000000000000 x7 7f7f7f7f7f7f7f7f
05-31 07:32:38.649 12175 12175 F DEBUG : x8 0000000000000083 x9 8aa80fbb5b3cecfd x10 0000000000000000 x11 0000000000000001
05-31 07:32:38.649 12175 12175 F DEBUG : x12 ffffffffffffffff x13 ffffffffffffffff x14 ff00000000000000 x15 ffffffffffffffff
05-31 07:32:38.649 12175 12175 F DEBUG : x16 0000002196029fa8 x17 000000438adf684c x18 0000000000000008 x19 0000000000002c18
05-31 07:32:38.650 12175 12175 F DEBUG : x20 0000000000002f8c x21 0000000000000001 x22 00000042cf373588 x23 00000042cf373588
05-31 07:32:38.650 12175 12175 F DEBUG : x24 0000000000000008 x25 00000042cf373588 x26 00000042da2cbca0 x27 0000000000000002
05-31 07:32:38.650 12175 12175 F DEBUG : x28 0000000000000001 x29 00000042cf372030 x30 000000438adac0ac
05-31 07:32:38.651 12175 12175 F DEBUG : sp 00000042cf371ff0 pc 000000438adac0c8 pstate 0000000060000000
05-31 07:32:38.912 12175 12175 F DEBUG :
05-31 07:32:38.912 12175 12175 F DEBUG : backtrace:
05-31 07:32:38.912 12175 12175 F DEBUG : #00 pc 000000000001e0c8 /system/lib64/libc.so (abort+104)
05-31 07:32:38.913 12175 12175 F DEBUG : #01 pc 0000000000007f10 /system/lib64/liblog.so (__android_log_assert+304)
05-31 07:32:38.913 12175 12175 F DEBUG : #02 pc 00000000000156a8 /system/lib64/libutils.so (android::Looper::rebuildEpollLocked()+348)
05-31 07:32:38.913 12175 12175 F DEBUG : #03 pc 0000000000015508 /system/lib64/libutils.so (android::Looper::Looper(bool)+236)
05-31 07:32:38.913 12175 12175 F DEBUG : #04 pc 000000000011094c /system/lib64/libandroid_runtime.so (android::NativeMessageQueue::NativeMessageQueue()+160)
05-31 07:32:38.914 12175 12175 F DEBUG : #05 pc 000000000011127c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativeInit(_JNIEnv*, _jclass*)+28)
05-31 07:32:38.914 12175 12175 F DEBUG : #06 pc 000000000062d420 /system/framework/arm64/boot-framework.oat (offset 0x62d000) (android.os.Binder.clearCallingIdentity [DEDUPED]+144)
05-31 07:32:38.914 12175 12175 F DEBUG : #07 pc 0000000000003690 /dev/ashmem/dalvik-jit-code-cache (deleted)
总结一句话,合理的规范的去写自己的代码,避免错误的发生。
参考:
https://blog.csdn.net/sdn_prc/article/details/28661661