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() 函数后,已验证该问题已修复。


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

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui