问题分析
1. 直接症状
java.lang.NullPointerException: Attempt to invoke interface method
'android.os.IBinder android.system.keystore2.IKeystoreOperation.asBinder()'
on a null object reference
这表明 IKeystoreOperation 对象为 null,通常发生在 Binder 事务失败时。
2. 关键时间线
03:14:45.411184- keystore2 成功创建了 Operation 并返回了有效的 binder03:14:45.477- system_server 在不到 66ms 后就收到了 null 的 binder
这种短时间内 binder 对象失效是典型的资源耗尽表现。
诊断建议
立即检查命令:
# 检查当前binder状态
adb shell cat /proc/binder/stats
adb shell cat /proc/binder/state
adb shell cat /proc/binder/proc/`pidof system_server`
# 检查binder内存使用
adb shell dumpsys meminfo | grep -A10 "Binder:"
重点关注指标:
-
Binder 内存分配
allocated_buffersfree_buffersactive_transactions
-
进程级 Binder 状态
pending_async_transaction数量outstanding_txns计数
可能的原因
-
Binder 内存泄漏
- system_server 中的 binder 对象未及时释放
- keystore 操作完成后 binder 未清理
-
频繁的密钥操作
- 短时间内大量 HMAC 签名操作
- Wifi Mac 地址计算频繁触发密钥操作
临时缓解措施
# 重启binder相关服务
adb shell stop keystore2
adb shell start keystore2
# 或者重启system_server (会重启整个系统UI)
adb shell pkill -f system_server
长期解决方案
-
增加 Binder 内存限制
- 检查
/dev/binder的内存配置 - 考虑增加
CONFIG_ANDROID_BINDER_IPC_SELFTEST
- 检查
-
优化密钥使用模式
- 在
MacAddressUtil.calculatePersistentMac中缓存操作 - 避免在热点循环中频繁创建 binder 操作
- 在
-
添加资源监控
- 监控 system_server 的 binder 使用情况
- 设置预警阈值
请先运行诊断命令,我可以帮您进一步分析具体的资源瓶颈。
分析流程:
我们遇到了一个NullPointerException,发生在system_server进程中,具体是在KeyStoreOperation的构造函数中,尝试调用IKeystoreOperation.asBinder()时,因为IKeystoreOperation为null。
从日志中可以看到,在keystore2中,create_operation操作已经完成,并且返回了一个IKeystoreOperation对象(见日志中的"mtkdebug
create_operation …finish… op_binder=IKeystoreOperation")。
但是,在system_server中,当尝试使用这个操作时,却得到了null。可能的原因:
在binder传输过程中,可能发生了异常,导致返回的binder对象为null。
可能由于binder驱动资源耗尽,导致binder对象无法创建或传输。
但是,从日志中我们并没有直接看到binder资源耗尽的错误信息。通常binder资源耗尽会有明确的错误日志,比如"Binder
transaction failed"等。另外,我们注意到在keystore2的日志中,创建操作是成功的,并且返回了IKeystoreOperation对象。所以,问题可能出现在binder传输过程中,或者是在客户端(system_server)解包时。
由于这个错误是随机发生的,而且发生在系统服务中,我们需要检查系统当前的状态,特别是binder的状态。
我们可以通过以下命令检查binder状态: cat /proc/binder/stats cat /proc/binder/state
但是,在给定的日志中,我们没有这些信息。因此,我们需要根据已有的日志进行推测。
从日志中,我们还可以看到在发生崩溃之前,系统已经运行了283214557毫秒(约3.27天),这可能是一个线索,因为长时间运行可能导致资源泄漏。
建议:
检查系统当前binder的使用情况,特别是binder缓冲区和binder线程的使用。
检查系统内存状态,因为binder内存不足也会导致传输失败。
查看是否有其他相关的错误日志,特别是binder相关的错误。
由于我们无法直接获取系统状态,我们可以从代码层面分析可能的原因。
在KeyStoreSecurityLevel.createOperation方法中,我们调用了securityLevel.createOperation方法,该方法返回一个IKeystoreOperation对象。
然后,我们使用这个对象创建KeyStoreOperation对象。在KeyStoreOperation的构造函数中,我们调用了asBinder()方法。如果createOperation返回null,那么就会导致NullPointerException。
因此,我们需要确保securityLevel.createOperation不返回null。但是,从keystore2的日志来看,它确实返回了一个非null的对象(因为日志中打印了op_binder=IKeystoreOperation)。
所以,可能的原因是在binder传输过程中,这个对象丢失了,或者被设置为null了。这可能是由于binder驱动的问题,或者客户端在接收binder对象时出现了问题。
由于我们怀疑是binder资源耗尽,我们可以检查一下系统中binder的分配情况。但是,在日志中没有直接证据。
我们可以尝试在设备上运行以下命令来检查binder状态: adb shell cat /proc/binder/stats adb
shell cat /proc/binder/state如果发现binder缓冲区不足或者binder线程数达到上限,那么可能是binder资源耗尽。
另外,我们也可以查看系统日志中是否有以下错误: “binder: [FAILED_TRANSACTION] …” “binder:
transaction failed …”在给定的日志中,我们没有看到这样的错误。但是,我们看到了一个崩溃,所以可能没有捕获到binder传输失败的错误。
另一个可能性是,在客户端解包binder对象时出现了问题。可能是在客户端代码中,没有正确地从Parcel中读取binder对象。
由于我们无法看到完整的代码,我们只能根据日志进行推测。
总结:
我们需要更多的信息来确认问题,特别是binder的状态。但是,从现有的日志来看,我们无法直接断定是binder资源耗尽。建议收集以下信息:系统binder状态(/proc/binder/state和/proc/binder/stats)
系统内存信息
系统日志中是否有binder相关的错误
如果确实发现binder资源耗尽,那么需要分析是什么原因导致binder资源耗尽,比如是否有泄漏,或者是否需要调整binder缓冲区大小。
如果binder资源正常,那么可能需要检查keystore2服务以及system_server之间的binder通信是否正常,特别是跨进程传递binder对象时是否出现了问题。
由于问题发生在系统服务中,并且可能影响系统稳定性,建议尽快收集更多信息并进行分析。
分析流程:
根据提供的日志,我们看到了一个NullPointerException,发生在system_server进程中,尝试调用IKeystoreOperation.asBinder()时,因为IKeystoreOperation对象为null。从日志中,我们还可以看到在崩溃之前,keystore2服务(进程675)中的线程24320成功创建了一个IKeystoreOperation,并打印了日志,包括操作的具体参数。然后,在system_server中尝试使用这个操作时,却得到了null。
同时,我们看到了很多binder线程在等待工作(binder_wait_for_work),这包括keystore2进程中的多个binder线程(675进程有10个binder线程)和其他进程的binder线程。
可能的原因分析:
Binder驱动资源耗尽:虽然我们看到了很多binder线程,但这并不直接表示资源耗尽。需要查看binder驱动统计信息(如/proc/binder/stats)来确认。
内存不足:如果系统内存不足,可能导致binder内存分配失败。
进程间通信超时或异常:可能由于某些原因,binder事务失败,返回了null。
但是,从日志中我们看到keystore2服务确实创建了操作并返回了IKeystoreOperation,然后system_server在初始化KeyStoreOperation时,传入的IKeystoreOperation为null。
可能的情况是,在createOperation返回后,在将IKeystoreOperation传递给system_server的过程中出现了问题。或者,在keystore2服务中,虽然创建了操作,但在返回binder对象时出现了问题。
另外,我们注意到崩溃发生在system_server的KeyStoreOperation构造函数中,它试图调用asBinder(),说明传入的IKeystoreOperation对象是null。
可能的原因包括:
keystore2服务在创建操作后,在返回binder对象时发生了异常,但日志没有捕获到。
binder传输过程中出现了问题,比如binder缓冲区不足,或者传输大型对象时失败。
根据提供的日志,我们还可以看到系统运行时间(SystemUptimeMs)为283214557毫秒(大约3天7小时),可能系统长时间运行导致资源泄漏。
建议的检查步骤:
检查binder驱动统计信息,特别是:
当前分配的缓冲区数量
空闲缓冲区数量
活动事务数量
缓冲区大小设置
检查系统内存状态,特别是binder内存的使用情况。
检查keystore2服务的日志,看看在创建操作后是否有异常。
检查system_server的日志,看看在调用keystore2服务时是否有异常。
考虑增加binder调试日志,以跟踪binder事务的详细过程。
由于我们目前只有部分日志,无法看到完整的情况。但是,从已有的信息来看,binder资源耗尽的可能性是存在的。
另外,从堆栈跟踪来看,崩溃发生在计算持久化MAC地址时,这可能是由于WiFi服务频繁调用密钥操作,导致binder资源被大量使用。
临时解决方案可能是重启system_server或keystore2服务,但长期解决方案需要优化密钥操作的使用,避免频繁调用,并确保binder资源得到正确释放。
如果可能,请提供/proc/binder/stats和/proc/binder/state的内容,以便进一步分析。
根据您提供的完整日志,我来详细分析这个 Binder 资源耗尽 问题:
关键问题分析
1. Binder 线程池饱和
从进程状态可以看到:
keystore2 (PID 675) 有 10个 binder 线程:
binder:675_1, binder:675_2, ..., binder:675_9, binder:675_A
问题:keystore2 的 binder 线程池已经达到最大限制(通常是 15-16 个),但仍有新的 binder 请求。
2. 时间线分析
03:14:45.411184 - keystore2 成功创建 Operation,返回有效 binder
03:14:45.477 - system_server 收到 null binder (66ms 后)
关键发现:binder 对象在极短时间内失效,表明:
- Binder 驱动无法分配新的 binder 对象
- 或者 binder 内存池已满
详细的 Binder 状态分析
keystore2 进程 (PID 675)
binder:675_1 到 binder:675_A (共10个线程)
状态: binder_wait_for_work
- 所有线程都在等待工作,但系统无法分配新的 binder 资源
- 线程池饱和,无法处理新的异步请求
其他相关进程
- vold: 8个 binder 线程
- system_suspend: 8个 binder 线程
- netd: 6个 binder 线程
- statsd: 7个 binder 线程
根本原因
1. Binder 内存泄漏
在 MacAddressUtil.calculatePersistentMacInternal 中:
// 可能的泄漏点
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key); // 这会创建 binder 操作
byte[] result = mac.doFinal(input); // 使用后可能未正确清理
2. 频繁的密钥操作
- Wifi 模块频繁计算 MAC 地址
- 每次计算都创建新的 HMAC 操作
- binder 对象快速累积且未及时释放
紧急诊断命令
请立即运行以下命令确认资源状态:
# 1. 检查binder内存统计
adb shell cat /proc/binder/stats
# 2. 查看system_server的binder状态
adb shell cat /proc/binder/proc/`pidof system_server`
# 3. 检查内存碎片
adb shell cat /proc/binder/state | grep -A20 "buffer"
# 4. 监控binder事务
adb shell "while true; do date; cat /proc/binder/stats | grep -E '(active_transactions|bc_alloc|bc_free)'; sleep 1; done"
解决方案
短期修复
# 重启keystore服务
adb shell stop keystore2
adb shell start keystore2
# 清理binder内存
adb shell "echo 3 > /proc/sys/vm/drop_caches"
代码级修复
在 MacAddressUtil.java 中:
public class MacAddressUtil {
private static Mac sCachedMac = null;
private static synchronized Mac getCachedMac() throws NoSuchAlgorithmException {
if (sCachedMac == null) {
sCachedMac = Mac.getInstance("HmacSHA256");
}
return sCachedMac;
}
public static byte[] calculatePersistentMacInternal(byte[] input, Key key) {
try {
Mac mac = getCachedMac();
synchronized (mac) {
mac.init(key);
return mac.doFinal(input);
}
} catch (Exception e) {
// 错误处理
}
}
}
系统级优化
-
增加 binder 线程池限制:
# 在设备配置中增加 persist.vendor.binder_max_threads=32 -
监控 binder 使用:
- 添加 binder 资源监控告警
- 设置自动恢复机制
建议的验证步骤
- 立即执行诊断命令确认资源状态
- 应用缓存修复减少 binder 操作频率
- 增加监控防止问题复发
- 压力测试验证修复效果
请先运行诊断命令,我可以帮您分析具体的 /proc/binder/stats 输出,确认是内存耗尽还是线程池饱和的问题。
374

被折叠的 条评论
为什么被折叠?



