嵌入式系统调试与性能分析
在嵌入式系统开发中,调试和性能分析是确保系统稳定运行和高效性能的关键环节。下面将详细介绍相关的工具和方法。
1. GDB调试
GDB是嵌入式系统开发者工具箱中一个强大的交互式调试工具。它稳定、文档丰富且广为人知。GDB可以通过在目标设备上放置代理来进行远程调试,对于应用程序可以使用gdbserver,对于内核代码可以使用kgdb。虽然其默认的命令行用户界面需要一些时间来适应,但有许多替代的前端工具,如TUI、DDD和Visual Studio Code,Eclipse也是一个流行的前端,通过CDT插件支持GDB调试。
另一种重要的调试方法是收集崩溃报告并离线分析,例如应用程序核心转储和内核Oops消息。
从Linux 3.5开始,内核日志缓冲区中的每一行都有一个16字节的二进制头,用于编码时间戳、日志级别等信息。相关讨论可参考:https://lwn.net/Articles/492125/ 。
2. 性能分析与追踪
交互式调试虽然能让我们深入了解程序的工作方式,但它将我们的视野局限在一小段代码上。为了从更宏观的角度查看系统是否按预期运行,我们需要进行性能分析和追踪。
2.1 技术要求
为了跟随示例进行操作,需要以下条件:
- 基于Linux的主机系统
- Buildroot 2020.02.9 LTS版本
- 适用于Linux的Etcher
- 微型SD卡读卡器和卡
- 树莓派4
- 5V 3A USB - C电源
- 用于网络连接的以太网电缆和端口
如果还未安装Buildroot 2020.02.9 LTS版本,可参考:https://buildroot.org/downloads/manual/manual.html 。所有示例代码可在:https://github.com/PacktPublishing/Mastering-Embedded-Linux-Programming-Third-Edition 的Chapter20文件夹中找到。
2.2 观察者效应
在使用工具进行性能分析之前,需要了解观察者效应。就像在许多领域中一样,测量某个属性会影响观察结果本身。在性能分析中,每次系统观察都会消耗CPU周期,这些资源不再用于应用程序。测量工具还会影响缓存行为、占用内存空间并写入磁盘,从而使情况变得更糟。因此,测量必然会带来开销。
为了减少这种影响,应尽量在目标设备上使用软件的发布版本进行测量,使用有效的数据集,并尽量减少额外服务的运行。同时,一些工具可能需要特殊的内核选项、调试符号、特定的编译标志或程序插桩,这会改变被观察的系统,使测量结果与生产系统的关联性变得困难。因此,建议采用观望的方法,仅在明确需要时进行更改。
2.3 开始性能分析
当查看整个系统时,一个好的起点是使用像top这样的简单工具,它能快速提供系统的概述,显示内存使用情况、哪些进程在消耗CPU周期以及这些情况在不同核心和时间上的分布。
- 如果top显示单个应用程序占用了用户空间的所有CPU周期,可以使用perf对该应用程序进行性能分析。
- 如果两个或更多进程的CPU使用率较高,可能存在数据通信等耦合因素。如果大量周期用于系统调用或处理中断,可能是内核配置或设备驱动存在问题,此时需要使用perf对整个系统进行性能分析。
- 如果想了解内核和其中事件的顺序,可以使用Ftrace、LTTng或BPF。
- 对于多线程代码中的锁定问题、随机数据损坏或内存泄漏等问题,Valgrind加上Helgrind插件可能会有所帮助。
2.4 使用top进行性能分析
top是一个简单的工具,不需要特殊的内核选项或符号表。BusyBox中有一个基本版本,procps包中有一个功能更强大的版本。也可以考虑使用htop,它功能与top类似,但用户界面更好。
top的摘要行显示了在各种状态下花费的时间百分比,例如:
| 状态 | 含义 |
| ---- | ---- |
| usr | 用户模式下的时间百分比 |
| sys | 系统模式下的时间百分比 |
| nic | 低优先级进程的时间百分比 |
| idle | 空闲时间百分比 |
| io | 等待I/O的时间百分比 |
| irq | 处理中断的时间百分比 |
| sirq | 处理软中断的时间百分比 |
以下是两个示例:
Mem: 57044K used, 446172K free, 40K shrd, 3352K buff, 34452K cached
CPU: 58% usr 4% sys 0% nic 0% idle 37% io 0% irq 0% sirq
Load average: 0.24 0.06 0.02 2/51 105
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
105 104 root R 27912 6% 61% ffmpeg -i track2.wav
在这个例子中,几乎所有时间(58%)都花在用户模式下,只有少量(4%)在系统模式下,说明系统在用户空间是CPU密集型的,问题出在ffmpeg应用程序上。
Mem: 13128K used, 490088K free, 40K shrd, 0K buff, 2788K cached
CPU: 0% usr 99% sys 0% nic 0% idle 0% io 0% irq 0% sirq
Load average: 0.41 0.11 0.04 2/46 97
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
92 82 root R 2152 0% 100% cat /dev/urandom
这个系统几乎所有时间(99%)都花在内核空间,是由于cat从/dev/urandom读取数据导致的。在这种情况下,单独对cat进行性能分析可能没有帮助,而对cat调用的内核函数进行分析可能会有效果。
默认情况下,top只显示进程信息,CPU使用率是进程中所有线程的总和。按H键可以查看每个线程的信息。如果使用procps版本的top,按1键可以查看每个CPU的摘要信息。一旦使用top找出了问题进程,就可以将GDB附加到该进程上。
2.5 穷人的性能分析器
可以使用GDB在任意间隔停止应用程序,以查看它正在执行的操作,这就是穷人的性能分析器。操作步骤如下:
1. 使用gdbserver(远程调试)或GDB(本地调试)附加到进程,进程会停止。
2. 观察进程停止的函数,可以使用GDB的backtrace命令查看调用栈。
3. 输入continue使程序继续运行。
4. 一段时间后,按Ctrl + C再次停止程序,然后回到步骤2。
通过多次重复步骤2 - 4,可以快速了解程序是在循环还是在前进,如果重复足够多次,还可以找出代码中的热点。相关信息可参考:http://poormansprofiler.org 。这是一种统计性能分析方法,其他统计性能分析工具还有perf record、OProfile和gprof。
2.6 引入perf
perf是Linux性能事件计数器子系统perf_events的缩写,也是与之交互的命令行工具的名称。自Linux 2.6.31起,它们就成为内核的一部分。相关文档可在Linux源代码树的tools/perf/Documentation目录以及:https://perf.wiki.kernel.org 中找到。
perf的核心是一组事件计数器,通过设置规则,可以捕获整个系统、仅内核或仅一个进程及其子进程的数据,并且可以在所有CPU或单个CPU上进行操作,非常灵活。
2.6.1 内核配置
为了完全启用perf,需要一个配置了perf_events的内核,并将perf命令交叉编译以在目标设备上运行。相关的内核配置选项是CONFIG_PERF_EVENTS,位于“General setup | Kernel Performance Events and Counters”菜单中。如果想使用跟踪点进行性能分析,还需要启用Ftrace部分描述的选项,同时启用CONFIG_DEBUG_INFO也是值得的。
perf命令有很多依赖项,交叉编译比较复杂,但Yocto Project和Buildroot都有针对它的目标包。此外,还需要在目标设备上为要分析的二进制文件提供调试符号,否则perf无法将地址解析为有意义的符号。理想情况下,整个系统(包括内核)都应该有调试符号,内核的调试符号在vmlinux文件中。
2.6.2 使用Yocto Project构建perf
如果使用标准的linux - yocto内核,perf_events已经启用,无需额外操作。要构建perf工具,可以将其明确添加到目标镜像依赖项中,或者添加tools - profile功能。同时,可能需要在目标镜像和内核vmlinux镜像中包含调试符号。在conf/local.conf中需要添加以下内容:
EXTRA_IMAGE_FEATURES = "debug-tweaks dbg-pkgs tools-profile"
IMAGE_INSTALL_append = "kernel-vmlinux"
2.6.3 使用Buildroot构建perf
许多Buildroot内核配置不包含perf_events,因此需要先检查内核是否包含前面提到的选项。要交叉编译perf,运行Buildroot的menuconfig并选择以下选项:
- BR2_LINUX_KERNEL_TOOL_PERF(位于Kernel | Linux Kernel Tools)
- BR2_ENABLE_DEBUG(位于Build options | build packages with debugging symbols菜单)
- BR2_STRIP = none(位于Build options | strip command for binaries on target菜单)
然后运行make clean,接着运行make。构建完成后,需要手动将vmlinux复制到目标镜像中。
2.6.4 使用perf进行性能分析
可以使用perf通过事件计数器对程序状态进行采样,并在一段时间内累积样本以创建性能分析报告。这是另一种统计性能分析方法,默认的事件计数器是cycles,它是一个通用的硬件计数器,映射到代表核心时钟频率周期计数的PMU寄存器。
使用perf创建性能分析报告分为两个阶段:
1. 使用perf record命令捕获样本并将其写入名为perf.data的文件(默认情况下)。例如:
# perf record sh -c "find /usr/share | xargs grep linux > /dev/null"
[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.368 MB perf.data (~16057 samples) ]
# ls -l perf.data
-rw------- 1 root root 387360 Aug 25 2015 perf.data
-
使用perf report命令分析结果。有三种用户界面可供选择:
- –stdio:纯文本界面,无用户交互,需要为每个跟踪视图启动perf report并进行注释。
- –tui:简单的基于文本的菜单界面,可在屏幕之间切换。
- –gtk:图形界面,功能与–tui类似。
默认情况下使用TUI界面。perf能够记录代表进程执行的内核函数,列表按最活跃的函数排序。如果vmlinux文件不在默认位置,可以在perf report命令中添加 - k
通过以上工具和方法,可以对嵌入式系统进行全面的调试和性能分析,确保系统的稳定性和高效性。
嵌入式系统调试与性能分析(续)
3. 事件追踪
除了性能分析,事件追踪也是了解系统运行情况的重要手段,下面介绍几种常用的追踪工具。
3.1 引入Ftrace
Ftrace是一种内核追踪工具,它可以帮助我们了解内核中的事件序列。不过,使用Ftrace需要对内核进行一些配置和重新编译。
- 内核配置 :为了使用Ftrace,需要在编译内核时启用相关选项。通常需要在内核配置中开启一些特定的追踪功能,例如在配置文件中设置相应的选项以支持内核函数追踪等。
-
使用方法
:启用Ftrace后,可以通过文件系统接口来控制和查看追踪信息。例如,可以通过写入特定的文件来选择要追踪的事件类型,然后从另一个文件中读取追踪数据。具体步骤如下:
-
挂载debugfs文件系统:
mount -t debugfs none /sys/kernel/debug -
选择要追踪的事件:例如,要追踪系统调用,可以向
/sys/kernel/debug/tracing/set_event文件写入syscalls。 -
开始追踪:向
/sys/kernel/debug/tracing/tracing_on文件写入1。 -
一段时间后,停止追踪:向
/sys/kernel/debug/tracing/tracing_on文件写入0。 -
查看追踪数据:读取
/sys/kernel/debug/tracing/trace文件。
-
挂载debugfs文件系统:
3.2 使用LTTng
LTTng(Linux Trace Toolkit Next Generation)是一个功能强大的追踪框架,它可以提供高分辨率的系统追踪信息。
-
安装
:可以通过包管理器来安装LTTng,例如在基于Debian或Ubuntu的系统上,可以使用
apt-get install lttng-tools lttng-modules-dkms命令进行安装。 -
使用步骤
:
-
创建会话:
lttng create mysession -
启用事件:例如,启用所有系统调用事件:
lttng enable-event -k syscalls:* -
开始追踪:
lttng start - 执行需要追踪的操作。
-
停止追踪:
lttng stop -
销毁会话:
lttng destroy mysession -
查看追踪数据:可以使用
babeltrace工具来解析和查看追踪数据,例如babeltrace /path/to/trace/directory。
-
创建会话:
3.3 使用BPF
BPF(Berkeley Packet Filter)最初用于网络数据包过滤,现在已经扩展到系统性能分析和追踪等领域。
- 原理 :BPF允许用户在内核中运行自定义的程序,这些程序可以在特定的事件发生时被触发,从而收集相关信息。
- 使用示例 :以下是一个简单的BPF程序示例,用于统计系统调用的次数:
from bcc import BPF
# 定义BPF程序
bpf_text = """
#include <uapi/linux/ptrace.h>
BPF_HASH(syscall_count);
int trace_syscalls(struct ptrace_regs *ctx) {
u64 zero = 0, *val;
u64 pid = bpf_get_current_pid_tgid();
val = syscall_count.lookup_or_init(&pid, &zero);
(*val)++;
return 0;
}
"""
# 加载BPF程序
b = BPF(text=bpf_text)
b.attach_kprobe(event="sys_enter", fn_name="trace_syscalls")
# 打印结果
while True:
try:
for k, v in b["syscall_count"].items():
print("PID %d: %d syscalls" % (k.value, v.value))
except KeyboardInterrupt:
break
这个示例使用Python的
bcc
库来编写和加载BPF程序,程序会在每次系统调用进入时被触发,统计每个进程的系统调用次数。
4. 使用Valgrind
Valgrind是一个内存调试和性能分析工具,它提供了多种工具集,其中Helgrind插件可以帮助检测多线程程序中的锁问题。
-
安装
:可以通过包管理器来安装Valgrind,例如在基于Debian或Ubuntu的系统上,可以使用
apt-get install valgrind命令进行安装。 -
使用步骤
:
-
内存调试
:例如,要检查一个程序是否存在内存泄漏,可以使用以下命令:
valgrind --leak-check=full ./your_program。Valgrind会在程序运行结束后输出详细的内存使用信息,包括是否存在内存泄漏以及泄漏的位置。 -
多线程调试
:如果要使用Helgrind插件检测多线程程序中的锁问题,可以使用以下命令:
valgrind --tool=helgrind ./your_program。Helgrind会在程序运行过程中检测锁的使用情况,并输出可能存在的问题。
-
内存调试
:例如,要检查一个程序是否存在内存泄漏,可以使用以下命令:
5. 使用strace
strace是一个简单的追踪工具,它可以揭示程序的执行过程,通过追踪程序进行的系统调用。
-
使用方法
:使用strace非常简单,只需要在命令前加上
strace即可。例如,要追踪ls命令的系统调用,可以使用以下命令:strace ls。strace会输出ls命令执行过程中进行的所有系统调用及其参数和返回值。
6. 总结
综上所述,在嵌入式系统的调试和性能分析中,我们有多种工具可供选择。以下是这些工具的使用场景总结:
| 工具 | 使用场景 |
| ---- | ---- |
| GDB | 交互式调试,定位程序中的错误 |
| top | 快速查看系统的整体运行情况,找出占用CPU或内存较多的进程 |
| perf | 对系统或应用程序进行性能分析,找出性能瓶颈 |
| Ftrace | 追踪内核中的事件序列,了解内核的运行情况 |
| LTTng | 高分辨率的系统追踪,适用于复杂系统的分析 |
| BPF | 自定义的系统追踪和性能分析,可用于特定场景的监控 |
| Valgrind | 内存调试和多线程程序的锁问题检测 |
| strace | 追踪程序的系统调用,了解程序的执行过程 |
在实际应用中,建议根据具体问题选择合适的工具,从简单的工具开始,逐步深入分析。同时,要注意测量的开销和对系统的影响,尽量在接近生产环境的条件下进行测量。通过合理使用这些工具,可以有效地提高嵌入式系统的稳定性和性能。
下面是一个简单的流程图,展示了在进行嵌入式系统调试和性能分析时的工具选择流程:
graph TD;
A[发现系统问题] --> B{问题类型};
B -->|整体性能问题| C[使用top查看系统概况];
C -->|单个应用问题| D[使用perf分析应用];
C -->|多个进程或内核问题| E[使用perf分析整个系统];
B -->|内核事件序列问题| F[使用Ftrace、LTTng或BPF];
B -->|多线程或内存问题| G[使用Valgrind];
B -->|程序执行过程问题| H[使用strace];
D -->|进一步调试| I[使用GDB];
通过这个流程图,我们可以更清晰地了解在不同情况下应该选择哪种工具进行调试和性能分析。希望这些信息对大家在嵌入式系统开发中有所帮助。
超级会员免费看
848

被折叠的 条评论
为什么被折叠?



