Android系统BUG:修改线程名目标错乱问题探究

背景

我们的APP在某次版本中 更新了腾讯地图SDK,更新之后发现在进入地图页面后,进程的native thread name被修改了。 进程名被修改为 nt-queue-291538

进入业务地图页面前,进程名为

进入业务地图页面后进程名变为 nt-queue-xxxx

问题分析

现象分析

经过反复验证,最终确认该问题的触发场景如下:

地图SDK中,这里第48行的eventQueue是一个线程,对线程调用了start()函数来启动线程。

由于我们引入的滴滴的booster插件,滴滴的booster会对所有线程的start函数做额外的插桩操作,在start函数之前,修改线程名,添加额外的类名信息,方便跟踪线程的创建点。修改后的字节码如下:

通过调试发现,这段业务逻辑会多次触发,并且每次eventQueue 线程都是同一个对象,正常情况下 对同一个线程多次调用start() 系统是会抛出异常的,但这里却能多次调用,这是因为SDK重写了该线程的start()函数,在内部判断线程是否已启动,如果已启动 就不会调用系统的start()函数。

这样的逻辑最终导致多次调用setName(),出现了 "对一个已经start的线程 调用修改线程名"的操作。

那这会有什么差异点呢?从setName的源码可以看出,如果目标线程是未启动完成的,则只会修改Java层的name属性,最后系统线程名会在native层线程真正启动后被修改。 而如果线程是已启动的(isAlive()为true),此时会额外直接调用setNativeName()进行线程名修改,因为此时底层的native thread已经存在了。

场景复现

typescript 复制代码
public class MainActivity extends Activity {
    private Thread bgThread;

    @Override
    protected void onCreate( Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new FrameLayout(this));
        startBgThread();
        modifyBgThreadOnMainThread();
    }

    //创建一个子线程,并启动
    private void startBgThread(){
        bgThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
        },"bgThread");
        bgThread.start();
    }

    //在主线程修改子线程的名称
    private void modifyBgThreadOnMainThread(){
        Handler bgHandler = new Handler(Looper.getMainLooper());
        bgHandler.post(new Runnable() {
            @Override
            public void run() {
                bgThread.setName("zxw");
            }
        });
    }
}

以上代码可以稳定复现该场景,通过调试发现,当对一个线程调用setName修改线程名时,如果该线程是已经启动的最终会影响的不是目标线程,而是当前线程 。 而此时由于当前执行环境在主线程 ,最终导致修改了主线程的系统线程名,主线程和进程是同一个线程,因此此时通过 /proc/${pid}/stat 获取线程名时发现进程名变化了

系统BUG原理解析

那么setNativeName 底层具体做了什么导致修改目标线程名时最终会影响的是当前线程呢?跟踪对应版本的系统源码

可以发现,SetThreadName 中最终是通过 pthread_setname_np修改线程名的,但这里修改线程名时传入的是 pthread_self() ,也就是当前线程自身 ,最终导致修改了错误的线程。

问题修复

从上节可以分析出,这明显是一个系统BUG,在跟踪了最新版本的aosp实现后,我们发现这个问题其实已经在 Android 15.0.0_r6 版本被修复了。变更逻辑如下

现在,调用art::SetThreadName时,会传入目标线程的pthread。

最终 调用pthread_setname_np时 会使用传入的pthread,从而修复了该问题。

其实从代码逻辑上来说这是一个很简单的BUG,但直到Android 15.0.0_r6 才被修复

结论

这个BUG只存在于 Android 15.0.0_r5 及更早的版本中

  • Android 15.0.0_r5 及之前:❌ 跨线程修改线程名时,如果目标线程已启动,则会错误地修改当前线程的线程名,而不是目标线程。
  • Android 15.0.0_r6 及之后:✅ 已修复,可以正确跨线程修改线程名

通常,我们修改线程名是在线程启动之前,或者是在目标线程上下文内部修改线程名,这样不会导致这个BUG出现。此次的问题发生原因是 三方地图SDK 重写了start()函数,又多次调用了start函数,导致滴滴的booster插件添加的setName逻辑也被多次触发,而此时调用setName的线程刚好是主线程,因此最终影响了 主进程名称

在同步腾讯地图SDK 避免多次调用start() 函数后,已验证该问题已修复。


在业务侧如果希望全局统一避免类似该问题的发生,可以做全局的插桩,在修改线程名时判断当下线程是否已启动,并且当前线程和目标线程是否不一致,如果不一致则不进行线程名修改,并做日志告警。

相关推荐
二流小码农2 小时前
鸿蒙开发:支持自定义组件的跑马灯
android·ios·harmonyos
用户41659673693552 小时前
优化 WebView 图片长按体验:JS Bridge 实现原生与网页端分发机制
android
Jeled3 小时前
RecyclerView ViewHolder 复用机制详解(含常见错乱问题与优化方案)
android·学习·面试·kotlin
2501_915106324 小时前
iOS 抓包全流程指南,HTTPS 抓包、TCP 数据流分析与多工具协同的方法论
android·tcp/ip·ios·小程序·https·uni-app·iphone
程序员 _孜然5 小时前
【最详细】android-studio-2025.2.x新版本,导出apk,含jks证书生成
android·ide·android studio
珹洺5 小时前
Java-Spring实战指南(三十四)Android Service实现后台音乐播放功能
android·java·spring
柯南二号5 小时前
【大前端】【Android】 Kotlin 语法超详细解析(2025 最新)
android·kotlin
四维碎片12 小时前
【Qt】配置安卓开发环境
android·开发语言·qt
百***992412 小时前
MySql的慢查询(慢日志)
android·mysql·adb