一、快速定位问题(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