CPU飙升到100%,如何排查(C++代码)

一、快速定位问题(5分钟内)

1. 确认基本情况

# Linux 查看进程整体 CPU
top -p <pid>

# 查看线程级别 CPU(找出是哪个线程占用高)
top -H -p <pid>
# 或使用 htop(更直观)
htop -p <pid>

# 查看线程详细信息
ps -L -p <pid> -o pid,tid,pcpu,comm

# Windows 使用任务管理器或 Process Explorer
# 右键进程 -> 属性 -> 线程,查看各线程 CPU

2、判断问题类型

  • 单线程 100%:通常是死循环、忙等待、算法复杂度问题
  • 多线程都高:可能是锁竞争、并发设计问题
  • 周期性飙升:可能是定时任务、GC 类操作、资源泄漏累积

二、使用性能分析工具定位热点

# 1. 实时查看热点函数
perf top -p <pid> 

# 2. 采样 10 秒,记录调用栈
perf record -F 99 -p <pid> -g -- sleep 10

# 4. 查看报告(找出热点函数)
perf report -i perf.data --no-children

# 4. 生成火焰图(需要 FlameGraph 工具)
perf script > out.perf
./flamegraph.pl out.perf > flame.svg

1、 实时查找热点函数

# 监控指定进程
sudo perf top -p <pid>

# 示例输出:
Samples: 1M of event 'cycles', 4000 Hz, Event count (approx.): 712345678
Overhead  Shared Object            Symbol
  45.67%  high_cpu_demo            [.] cpu_intensive_task()     # ← 热点函数1
  22.31%  high_cpu_demo            [.] cache_unfriendly_task()  # ← 热点函数4
  18.45%  libc.so.6                [.] __sqrt_fma
   5.12%  [kernel]                 [k] _raw_spin_lock
   3.21%  high_cpu_demo            [.] lock_contention_task()   # ← 热点函数3
   2.10%  [kernel]                 [k] finish_task_switch

2、详细采样记录

# 记录30秒的数据
sudo perf record -g -p <pid> -- sleep 30

# 记录特定事件(如缓存缺失)
sudo perf record -g -p <pid> -e cache-misses -- sleep 30

# 记录调用栈(帧指针优化需关闭或使用dwarf)
sudo perf record -g --call-graph dwarf -p <pid> -- sleep 30

# 高频率采样(可能生成大文件)
sudo perf record -F 99 -g -p <pid> -- sleep 30

3、分析采样数据,生成报告

# 分析perf.data
sudo perf report

# 输出示例:
#     Overhead  Command          Shared Object        Symbol
# 1.  45.67%   high_cpu_demo    high_cpu_demo       cpu_intensive_task
# 2.  22.31%   high_cpu_demo    high_cpu_demo       cache_unfriendly_task
# 3.  18.45%   high_cpu_demo    libm.so.6           __sqrt_fma

4、实际分析案例

# 步骤1:找到问题进程
top -H  # 发现进程12345占用高CPU
ps -T -p 12345  # 查看线程

# 步骤2:快速热点分析
sudo perf top -p 12345
# 发现热点在do_heavy_computation()函数

# 步骤3:详细采样
sudo perf record -g -F 99 -p 12345 -- sleep 60
# 生成perf.data文件

# 步骤4:分析调用链
sudo perf report -g graph --no-children
# 发现调用链:main → process_data → do_heavy_computation → 低效算法

# 步骤5:性能统计
sudo perf stat -p 12345 \
    -e cycles,instructions,cache-misses,branch-misses \
    -- sleep 10
# 发现CPI高(cycles per instruction),cache-miss率高

# 步骤6:生成火焰图
sudo perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > hotspot.svg
# 用浏览器打开svg,直观看到函数调用关系和耗时比例

二、使用GDB调试定位问题

1. 编译时带调试符号

# 必须加 -g 参数,否则看不到源代码和变量
g++ -g -O0 main.cpp -o myapp          # 调试版本(无优化)
g++ -g -O2 main.cpp -o myapp_release  # 生产版本(带优化但保留符号)

# 多线程程序记得加 -pthread
g++ -g -O0 -pthread main.cpp -o myapp

2. GDB 启动方式

# 方式1:直接启动程序
gdb ./myapp

# 方式2:附加到运行中的进程(最常用)
gdb -p <pid>
# 或
gdb attach <pid>

# 方式3:分析 core dump
gdb ./myapp core.12345

3、查看所有线程状态

(gdb) info threads
# 示例输出:
  Id   Target Id                                 Frame 
* 1    Thread 0x7ffff7dbb740 (LWP 12345) "cpu_problem_demo" 0x00007ffff7e7a9f3 in __GI___libc_read 
  2    Thread 0x7ffff6db9700 (LWP 12346) "cpu_problem_demo" infinite_loop_thread () at cpu_problem_demo.cpp:15
  3    Thread 0x7ffff65b8700 (LWP 12347) "cpu_problem_demo" 0x00007ffff7f874ca in __lll_lock_wait ()
  4    Thread 0x7ffff5db7700 (LWP 12348) "cpu_problem_demo" 0x00007ffff7f874ca in __lll_lock_wait ()
  5    Thread 0x7ffff55b6700 (LWP 12349) "cpu_problem_demo" recursive_function (depth=998) at cpu_problem_demo.cpp:58
  6    Thread 0x7ffff4db5700 (LWP 12350) "cpu_problem_demo" busy_waiting_thread () at cpu_problem_demo.cpp:78
  7    Thread 0x7ffff45b4700 (LWP 12351) "cpu_problem_demo" 0x00007ffff7e7e8f5 in __GI___nanosleep

4、查看线程调用栈

# 查看所有线程的调用栈
(gdb) thread apply all bt

# 查看特定线程的栈
(gdb) thread 2
(gdb) bt
# 或简写
(gdb) bt 2

# 查看完整的调用栈(显示所有帧)
(gdb) bt full

# 限制栈深度
(gdb) bt 5  # 只显示5层

5、示例

# 切换到疑似死循环的线程(比如线程2)
(gdb) thread 2
(gdb) bt
# 输出:
# #0  infinite_loop_thread () at cpu_problem_demo.cpp:15
# #1  0x0000555555556a5d in std::__invoke_impl<void, void (*)()> (__f=0x55555555b1a0 <infinite_loop_thread()>)
# #2  0x00005555555569c2 in std::__invoke<void (*)()> (__fn=0x55555555b1a0 <infinite_loop_thread()>)
# #3  0x0000555555556962 in std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul> (this=0x55555556aec8)
# #4  0x0000555555556939 in std::thread::_Invoker<std::tuple<void (*)()> >::operator() (this=0x55555556aec8)
# #5  0x000055555555691d in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (this=0x55555556aec0)

# 查看当前代码位置
(gdb) frame 0
# 输出:
# #0  infinite_loop_thread () at cpu_problem_demo.cpp:15
# 15              while (running) {

# 查看循环变量
(gdb) info locals
# 输出:
# count = 42367
# result = 1.34924880923e+12
# i = 32891

# 查看源代码上下文
(gdb) list 10,30
# 可以看到整个循环结构

# 检查循环条件
(gdb) p running
# 输出:$1 = true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值