arm64 UOS白屏/CPU占用100%问题分析之入门篇

您所在的位置:网站首页 天翼云cpu arm64 UOS白屏/CPU占用100%问题分析之入门篇

arm64 UOS白屏/CPU占用100%问题分析之入门篇

2024-07-13 13:17| 来源: 网络整理| 查看: 265

问题表现

应用白屏/卡住/应用进程的某条或多条线程cpu占用接近100%,目前arm64 UOS云电脑(host机器为华为鲲鹏920)中,易连/浏览器/资源管理器/向日葵等都有这个问题

问题的直接原因

cpu(疑似)在用户空间重复执行同一条指令,该指令为普通的非特权指令,且指令语义并非重复执行自己

分析过程(以浏览器为例)

1. 从top看浏览器进程(pid为11104)两条线程(tid为11104和11110)cpu接近占满且基本都在用户空间运行

2. 针对这种情况,往往需要通过perf查看热点函数进一步定位,以11104为例,通过perf top -t 11104看到热点函数在v8::internal::Assembler::Assembler,第一感觉看起来是在这个函数死循环了

如果你是第一次分析此问题,此时正确的方式是翻看v8::internal::Assembler::Assembler的代码Assembler::Assembler(const AssemblerOptions& options,                     std::unique_ptr buffer)    : AssemblerBase(options, std::move(buffer)),      unresolved_branches_(),      constpool_(this) {  veneer_pool_blocked_nesting_ = 0;  Reset();

#if defined(V8_OS_WIN)  if (options.collect_win64_unwind_info) {    xdata_encoder_ = std::make_unique(*this);  }#endif}子函数的代码这里就不贴了,因为分析代码这种方式对于本问题并不适用,每次出现这个问题的时候"死循环"的位置都不一样,并且由于我们也没有浏览器的代码,所以高效且精准的方式应该是结合汇编和代码来一起看(如果可以的话可以确定下chromium的具体tag),如何得到热点汇编呢?应该通过perf找到热点ip寄存器的值(通常会有很多,可通过它们的值定位到具体函数的具体位置),然后再反编译对应模块的代码来分析,具体步骤如下:sudo sysctl -w  kernel.perf_event_max_sample_rate=4000 //调整下采样频率上限sudo perf record -t 11104 -F 3999 //用3999的频率采样,这也是为了避免跟tick周期重复产生干扰sudo perf script -F ip,dso,sym //获取ip地址和符号以及模块信息理论上获取的ip会相对分散但一定会收敛到某个循环内,然而事实大相径庭: aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser) aaaad1574000 v8::internal::Assembler::Assembler (/usr/share/browser/browser)用户空间所有的ip都在同一地址0xaaaad1574000,而内核地址则是分散的:ffff0000080818dc __softirqentry_text_start ([kernel.kallsyms])ffff000008a2be80 inet_gro_receive ([kernel.kallsyms])ffff00000860cc30 virtqueue_get_buf_ctx ([kernel.kallsyms])赶紧看一下0xaaaad1574000处的指令到底是什么指令ctyun@0000000g-3F3F8cwE6I:~/cong$ cat /proc/11104/maps | grep "/usr/share/browser/browser"aaaacc30d000-aaaacf431000 r--p 00000000 fd:02 1714852                    /usr/share/browser/browseraaaacf431000-aaaad73c4000 r-xp 03124000 fd:02 1714852                    /usr/share/browser/browseraaaad73c4000-aaaad743e000 rw-p 0b0b7000 fd:02 1714852                    /usr/share/browser/browseraaaad743e000-aaaad7be2000 r--p 0b131000 fd:02 1714852                    /usr/share/browser/browser/usr/share/browser/browser的基地址为0xaaaacc30d000,得出偏移为0x5267000,反编译/usr/share/browser/browser并定位到0x5267000偏移处:

看到二进制中的指令为:.text:0000000005267000 F4 4F 02 A9                 STP             X20, X19, [SP,#0x30+var_10]指令的语义为:将寄存器X20和X19存到sp+0x20处,就是一个普通的保存上下文的操作,怎么会一直在执行这条指令呢,难道内存被篡改了?写了一个获取进程内存的工具print_memory,用于获取指定进程指定地址指定大小的内存,并按字节输出:./print_memory 11104 0xaaaad1573ff8 40pid:11104addr:0xaaaad1573ff8len:40FF 03 01 D1 F5 0B 00 F9 F4 4F 02 A9 FD 7B 03 A9 FD C3 00 91 68 2E 03 D0 08 61 42 F9 F3 03 00 AA 08 01 40 F9 E8 07 00 F90xaaaad1573ff8是函数头的地址,可以看到这片内存跟二进制中的一样,未被篡改,而根据指令的语义,执行完这条指令以后一定会继续执行下一条指令,这无法解释。于是提出了如下几点怀疑并分别进行了验证:1,perf时间太短,刚被调度到准备执行又被perf的中断打断?延长perf时间,perf结果数百万次,结果依旧2,perf工具本身的结果就不可靠?int main(){while(1){asm("nop");}return 0;}用上面的demo跑了一下,然后用同样的办法perf可以得到"相对正确"的结果,ip为:    ......           400558           400558           400558           400558           400558           400554           400558           400558           400558           400558           400558           400558           400558           400558           400558    ......看下汇编:0000000000400554 :400554:       d503201f        nop400558:       17ffffff        b       400554 40055c:       d503201f        nop大部分都会是0x400558,只有少数0x400554,猜测应该跟cpu的pipeline有关,又改了下代码:int main(){while(1){asm("nop");asm("nop");asm("nop");asm("nop");}return 0;}再用同样的方式perf,得到的结果为:           400558           400558           400554           400554           400558           400558           400558           400554           400554           400558           400558           400558           400558           400558           400554           400558    ......    400560    ......对应的汇编代码为:0000000000400554 :400554:       d503201f        nop400558:       d503201f        nop40055c:       d503201f        nop400560:       d503201f        nop400564:       17fffffc        b       400554 大部分是0x400554和0x400558,有少数0x400560,没有0x40055c,大概率是跟cpu pipeline有关,上述实验说明,perf的结果还是相对比较可靠的,如此小的循环也可以perf出相对正确的结果3,内核的bug导致perf的结果出现假象?有可能,但没有直接的证据,需要阅读perf的源码看下其原理,理论上其基于pmu中断,以此来采样结果相对还是可靠的,而且perf被大量使用,如果有bug早应该被修复了才对,所以看起来也不太像。如果是内核bug导致,那必然是在出问题的进程的内核路径上产生的问题4,cpu的bug?看起来不太像,首先程序如果进入内核态,perf的结果相对正常,只有在用户态会这样,理论上cpu的状态对于内核态和用户态区别应该不大,但也不是完全没有区别,至少有些寄存器会不一样,所以没有办法完全排除cpu的问题——最好是由华为的人来验证,最直接的方式就是拿硬件调试器看看到底cpu上正在执行什么指令5,host/kvm的问题?就在本文档的时候,其中一个线程(tid 11104)已经恢复了,所以用另外一个线程(tid 11110)来验证一下,这里首先得确认11110这个线程是不是同样的问题,也是用上述同样的方式,发现是同样的问题,只是ip不一样而已:aaaad60d3fd4 blink::EventWithCallback::EventWithCallback (/usr/share/browser/browser)

.text:0000000009DC6FD4 FF 83 01 D1                 SUB             SP, SP, #0x60连上host机器,找到对应的kvm进程,从host上看cpu占用和guest的情况基本一致:host:

guest:

尝试从host上perf:sudo perf kvm --host --guest record -t 3286693

发现跟guest上的结果一致,问题点均为0xaaaad60d3fd4,host和guest的的perf结果一致又增加了一个可疑点,如果是内核bug,那是host内核bug还是guest内核bug?这又得看看perf kvm的实现原理了,阶段性总结一下目前的怀疑点:1,内核的bug(host或guest)2,硬件问题为了说服自己这不是一个应用程序的问题,于是用gdb看了下上下文:

跟二进制里面的依旧是一致的

就是从0xaaaad60ce400这里跳转到0xaaaad60d3fd4,然后执行第一条指令就卡住了,这又和反编译的结果一致: 所以从应用程序的角度看,就是自己应用的指令执行卡住了而且貌似一致在执行那条指令,诡异的是gdb单步执行又可以继续执行,看起来是gdb停止task的动作对**有影响导致现场恢复,这个**是cpu还是内核还是应用呢?。。。索性直接continue,发现在另外一个地方又卡住了,然后再continue又恢复正常了。。。综上目前怀疑的点有两个:1,内核bug——看perf代码,我方可进行  ——review内核的改动和patch,需要uos方来进行,我方可参与2,硬件问题——硬件调试器调试确认,需要华为协助,我方可参与


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3