Debugging with the Yocto Project
The debug process is an essential step in every development cycle. In this chapter, we will learn how to configure Poky to help us with the debugging process; for example, how we can configure our system to provide the tools needed for a remote debug using the Gnu DeBugger (GDB), how we can track our changes using
buildhistory, and how we can use handy debug tools, such as
oe-pkgdata-util,
bitbake-getvar, and
devshell.
1, Differentiating metadata and application debugging
在深入了解调试细节之前,我们需要了解调试的不同类型,如元数据调试和运行时代码调试。
Before we delve into the details of debugging, we need to realize that there are different types of debugging, such as metadata and runtime code debugging.
元数据调试是为了确保 BitBake 任务的行为与我们的目标一致,并在不一致时找出罪魁祸首。例如,可能需要修改配方以启用某个功能。在这种情况下,我们可以使用 BitBake 在主机中生成的多个日志文件来帮助追踪相关任务的执行路径。
Metadata debugging is needed to ensure that the behavior of BitBake’s tasks aligns with our goals and to identify the culprit when it’s not aligned. For example, a recipe may need to be fixed to enable a feature. In such a case, we can use several log files generated by BitBake in the host to help trace the execution path of the involved task.
另一方面,调试运行时代码更为自然,因为它与应用程序、库或内核的典型开发周期基本相同。根据我们要解决的问题,正确的工具可能从调试器到代码工具(例如,添加调试打印)不等。
On the other hand, debugging runtime code is more natural as it is essentially the same as the typical development cycle of an application, a library, or a kernel. Depending on the issue we are seeking to resolve, the right tool to help may vary from a debugger to code instrumentation (for example, adding debug prints).
2, Tracking image, package, and SDK contents
要确保我们拥有映像、软件包和软件开发工具包 (SDK) 以及预期内容,最简单的方法就是使用构建历史机制。
The easiest way to ensure we have the image, packages, and software development kit (SDK), along with the expected contents, is to use the
buildhistory mechanism.
当配方更新到新版本或代码发生变化时,可能会影响生成软件包的内容,进而影响映像或 SDK。
When a recipe is updated for a new version or has its code changed, it may influence the contents of the generated packages and, consequently, the image or SDK.
Poky 要处理许多配方,而图像或 SDK 往往有数十或数百个软件包。因此,跟踪软件包内容可能很有难度。帮助完成这项任务的 Poky 工具是 buildhistory。
Poky deals with many recipes and images or SDKs frequently have tens or hundreds of packages. Therefore, it may be challenging to track the package contents. The Poky tool that helps in this task is
buildhistory.
buildhistory,顾名思义,就是记录 Poky 使用过程中构建的多个工件的内容历史。它跟踪软件包、图像和 SDK 的构建及其内容。
buildhistory, as the name suggests, keeps a history of the contents of several artifacts built during the use of Poky. It tracks package, image, and SDK building and their contents.
要在系统中启用构建历史,我们需要在 build/conf/local.conf 文件中添加以下代码行:
To enable buildhistory in our system, we need to add the following lines of code in our
build/conf/local.conf file:
[ Figure 10.1 – How to enable buildhistory support ]
INHERIT 方法将 buildhistory 类钩子包含在构建过程中。同时,BUILDHISTORY_COMMIT 这一行能让 BitBake 为每一个新软件包、镜像或 SDK 的构建在 buildhistory 仓库中创建一个新的 Git 提交。通过 Git 提交,跟踪工作就像在两个提交之间使用 git diff 一样简单。数据以文本文件的形式保存在 build/buildhistory 目录下,方便使用。
The
INHERIT method includes the
buildhistory class hooks in the building process. At the same time, the
BUILDHISTORY_COMMIT line enables BitBake to create a new Git commit in the
buildhistory repository for every new package, image, or SDK build. The Git commit makes tracking as simple as using
git diff between two commits. The data is stored under the
build/buildhistory directory as text files for ease of use.
Poky 提供了一个名为 “buildhistory-diff ”的工具,能以更简洁的方式输出两个构建历史状态之间的差异,这在检查变更时非常有用。buildhistory-diff 工具能更有意义地输出任意两个 Git 修订版本之间的差异。
Poky provides a utility that outputs the difference between two
buildhistory states, called
buildhistory-diff, in a more concise way, which is very useful when checking for changes. The
buildhistory-diff utility outputs the difference between any two Git revisions more meaningfully.
例如,假设我们在 core-image-minimal 镜像中添加了 strace 软件包并构建它。在这种情况下,可以使用 buildhistory-diff 命令来检查结果变化,如下面的截图所示:
For example, suppose we add the strace package in the core-image-minimal image and build it. In that case, the buildhistory-diff command can be used to check the resultant changes, as in the following screenshot:
[ Figure 10.2 – The result of buildhistory-diff ]
对于每个软件包的构建,buildhistory 会创建生成的子软件包列表、安装脚本、文件所有权和大小列表、依赖关系等。此外,还会为镜像和 SDK 创建软件包、文件系统文件和依赖关系图之间的依赖关系。
For every package build, buildhistory creates a list of generated sub-packages, installation scripts, a list of file ownership and sizes, the dependency relation, and more. In addition, the dependency relationship between the packages, filesystem files, and dependency graph is created for images and SDKs.
3, Debugging packaging
在更复杂的配方中,我们会将安装的内容拆分成几个子包。子软件包可以是可选功能、模块或任何其他可选安装的文件集。
In more sophisticated recipes, we split the installed contents into several sub-packages. The sub-packages can be optional features, modules, or any other set of files that is optional to install.
要查看配方内容的拆分情况,我们可以使用 build/tmp/work/
///packages-split 目录。该目录包含每个子软件包的子目录,其内容位于子树中。
To inspect how the recipe’s content has been split, we can use the
build/tmp/work////packages-split directory. It contains a sub-directory for every sub-package and has its contents in the sub-tree.
在可能造成内容分割错误的原因中,我们确定了以下几点:
Among the possible reasons for a mistaken content split, we have defined the following:
* 未安装内容(例如,安装脚本出错)
* 应用程序或库配置错误(例如,禁用功能)
* 元数据错误(例如,软件包顺序错误)
* The contents not being installed (for example, an error in installation scripts)
* An application or library configuration error (for example, a disabled feature)
* Metadata errors (for example, the wrong package order)
另一个导致编译失败的常见问题是 sysroot 目录中缺少所需的工件(例如头文件或动态链接库)。在 build/tmp/work/
///sysroot-destdir 中可以看到与系统根生成对应的目录。
Another common issue for build failure is lacking the required artifacts in the
sysroot directory (for example, headers or dynamic libraries). The counterpart of the
sysroot generation can be seen at
build/tmp/work////sysroot-destdir.
如果这还不够,我们还可以利用这些日志功能对任务代码进行检测,以确定导致意外结果的逻辑错误或漏洞。
If this is not enough, we can instrument the task code with these logging functions to determine the logical error or bug that has caused the unexpected result.
Inspecting packages
Yocto 项目的核心是处理软件包。因此,项目设计了 oe-pkgdata-util 来帮助我们检查已构建的软件包和相关数据。例如,在运行 bitbake bluez5 之后,我们可以使用以下命令查找所有与 bluez 相关的软件包:
A central aspect of the Yocto Project is dealing with the packages. Therefore, the project has designed
oe-pkgdata-util to help us to inspect the built packages and related data. For example, after running
bitbake bluez5, we can use the following command to find all the packages related to
bluez:
[ Figure 10.3 – Listing all the available packages and filtering those related with bluez ]
有时,我们需要查找包含该特定文件的软件包。我们可以使用以下命令查询软件包数据库:
Sometimes, we need to find the package that includes this specific file. We can inquire about the packages database using the following command:
[ Figure 10.4 – Finding which package provides /usr/bin/rfcomm ]
另一种情况是,我们需要找出软件包的当前版本。这可以通过以下命令来完成:
Another use case is when we need to find out the current version of a package. This can be done with the following command:
[ Figure 10.5 – Listing the package info for bluez5 ]
We can also list all the files for the given package using the following command:
[ Figure 10.6 – Listing the files from the bluez5 package ]
oe-pkgdata-util 脚本是帮助我们调试打包的便捷工具。
The
oe-pkgdata-util script is a handy tool to help us debug packaging.
4, Logging information during task execution
BitBake 提供的日志工具可以方便地跟踪代码执行路径。BitBake 为 Python 和 Shell Script 代码提供了日志记录函数,具体如下:
The logging utilities provided by BitBake are handy for tracing the code execution path. BitBake provides logging functions for use in Python and Shell Script code, described as follows:
* Python 为在 Python 函数中使用,BitBake 支持多种日志级别,如 bb.fatal、bb.error、bb.warn、bb.note、bb.plain 和 bb.debug。
* Shell 脚本: 对于在 Shell 脚本函数中的使用,同样存在一组日志级别,并可通过类似的语法访问:bbfatal、bberror、bbwarn、bbnote、bbplain 和 bbdebug。
* Python: For use within Python functions, BitBake supports several log levels such as bb.fatal, bb.error, bb.warn, bb.note, bb.plain, and bb.debug.
* Shell Script: For use in Shell Script functions, the same set of log levels exists and is accessed with a similar syntax: bbfatal, bberror, bbwarn, bbnote, bbplain, and bbdebug.
这些日志功能非常相似,但也有细微差别,具体如下:
These logging functions are very similar to each other but have minor differences, described as follows:
* bb.fatal 和 bbfatal:这些日志信息的优先级最高,因为它们会打印信息并终止处理。它们会导致编译中断。
* bb.error 和 bberror: 显示错误,但不会强制停止编译。
* bb.warn 和 bbwarn: 向用户发出警告。
* bb.note 和 bbnote:向用户添加备注。它们仅供参考。
* bb.plain 和 bbplain: 输出一条信息。
* bb.debug 和 bbdebug: 添加调试信息,显示信息取决于所使用的调试级别。
* bb.fatal and bbfatal: These have the highest priority for logging messages as they print the message and terminate the processing. They cause the build to be interrupted.
* bb.error and bberror: These display an error but do not force the build to stop.
* bb.warn and bbwarn: These warn the users about something.
* bb.note and bbnote: These add a note to the user. They are only informative.
* bb.plain and bbplain: These output a message.
* bb.debug and bbdebug: These add debugging information that is shown depending on the debug level used.
使用 Python 和 Shell 脚本中的日志功能有一个微妙的区别。Python 中的日志功能由 BitBake 直接处理,可以在控制台中看到,并存储在 build/tmp/log/cooker/
中的执行日志中。在 Shell 脚本中使用日志功能时,信息会输出到一个单独的任务日志文件,该文件位于 build/tmp/work////temp 中。
There is one subtle difference between using the logging functions in Python and Shell Script. The logging functions in Python are directly handled by BitBake, seen on the console, and stored in the execution log inside
build/tmp/log/cooker/. When the logging functions are used in Shell Script, the information is outputted to an individual task log file, which is available in
build/tmp/work////temp.
在临时目录中,我们可以使用 run.
. 模式检查每个任务的脚本,并使用 log.. 模式查看其输出。符号链接使用 log. 模式指向最后的日志文件。例如,我们可以检查 log.do_compile,以验证在编译过程中是否使用了正确的文件。
Inside the temp directory, we can inspect the scripts for every task with the
run.. pattern and use the
log.. pattern for its output. Symbolic links point to the last log files using the
log. pattern. For example, we can check for
log.do_compile to verify whether the right files were used during the build process.
build/tmp/work 目录在第 6 章 “详细说明临时编译目录 ”中有详细介绍。
The
build/tmp/work directory is detailed in Chapter 6, Detailing the Temporary Build Directory.
5, Debugging metadata variables
要调试元数据变量,我们可以使用 bitbake-getvar 脚本。它使用 BitBake 内部数据来获取特定变量值及其归因历史。
To debug the metadata variables, we can use the
bitbake-getvar script. It uses the BitBake internal data to get a specific variable value and its attribution history.
例如,要检查 procps 配方的 PACKAGECONFIG 变量,我们可以使用以下命令:
For example, to inspect the
PACKAGECONFIG variable for the procps recipe, we can use the following command:
[ Figure 10.7 – The result of bitbake-getvar -r procps PACKAGECONFIG ]
从图 10.7 可以看出,结尾处的 PACKAGECONFIG 为空。我们还可以看到,在 meta/recipes-extended/procps/procps_3.3.17.bb 文件的第 33 行,defaultval 被设置为“${@bb.utils.filter(‘DISTRO_FEATURES’, ‘systemd’, d)}”。
From Figure 10.7, we can see that
PACKAGECONFIG at the end is empty. We can also see that
defaultval was set to
"${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)}" at line 33 from the
meta/recipes-extended/procps/procps_3.3.17.bb file.
我们可以从下面的截图中看到第 33 行和第 34 行的 procps 配方:
We can see the
procps recipe lines 33 and 34 in the following screenshot:
[ Figure 10.8 - The procps recipe 33 and 34 lines ]
bitbake-getvar 脚本可用于检查某个功能是否已启用,或确保某个变量已按我们的预期展开。
The
bitbake-getvar script can be used to check whether a feature is enabled or to be sure a variable has been expanded as we expect.
6, Utilizing a development shell
在编辑软件包或调试编译失败时,开发 shell 是非常有用的工具。使用 devshell 时,需要执行以下步骤:
A development shell can be a helpful tool when editing packages or debugging build failures. The following steps take place when we use
devshell:
1. 将源文件解压缩到工作目录中。
2. 打上补丁
3. 在工作目录中打开一个新终端。
1. Source files are extracted into the working directory.
2. Patches are applied.
3. A new terminal is opened in the working directory.
构建所需的所有环境变量都可以在新终端中使用,因此我们可以使用 configure 和 make 等命令。这些命令的执行就像构建系统在运行它们一样。
All the environment variables needed for the build are available in the new terminal, so we can use commands such as
configure and
make. The commands execute just as if the build system were running them.
下面的命令是在名为 linux-yocto 的目标机上使用 devshell 的示例:
The following command is an example that uses
devshell on a target named
linux-yocto:
[ Figure 10.9 – Running devshell for the linux-yocto recipe ]
图 10.9 中的命令允许我们重新修改 Linux 内核源代码、构建它,并根据需要修改其代码。在图 10.10 中,你可以看到执行 bitbake linux-yocto -c devshell 命令后的日志:
The command from Figure 10.9 allows us to rework the Linux kernel source code, build it, and change its code as needed. In Figure 10.10, you can see the log after executing the
bitbake linux-yocto -c devshell command:
[ Figure 10.10 – The log for bitbake linux-yocto -c devshell ]
Note
请务必记住,在 devshell 中进行的更改不会在两次构建之间持续存在;因此,在离开之前,我们必须小心记录任何关键更改。
It is crucial to remember that changes made inside devshell do not persist between builds; thus, we must be careful to record any critical change before leaving it.
既然我们掌握了源代码,就可以用它来生成额外的补丁。一个方便的方法是使用 Git 和 git format-patch 来创建补丁,然后将其包含在配方中。
Since we have the source at our disposal, we can use it to generate extra patches. A convenient way of doing that is using Git and git format-patch to create the patch to be included in the recipe afterward.
下面的截图显示了调用 devshell 任务后打开的 devshell 窗口:
The following screenshot shows the
devshell window open after calling the
devshell task:
[ Figure 10.11 – The list of files inside the WORKDIR directory ]
devshell 命令对于小型任务来说非常方便。但如果需要进行更复杂的更改,使用外部工具链或 devtool 可能是更好的选择。
The devshell command is convenient for small tasks. But when a more involved change is needed, using an external toolchain or
devtool might be a better option.
要将生成的补丁包含在配方中并使其持久化,请参阅第 13 章,自定义现有配方。
To include the generated patch in the recipe and make it persistent, see Chapter 13, Customizing Existing Recipes.
7, Using the GNU Debugger for debugging
在开发任何项目的过程中,我们都会时不时地纠结于如何理解细微的 bug。GDB 作为 Poky 中的一个软件包可用。它默认安装在 SDK 镜像中,详见第 9 章 Yocto 项目开发。
While developing any project, from time to time, we end up struggling to understand subtle bugs. The GDB is available as a package in Poky. It is installed in SDK images by default, as was detailed in Chapter 9, Developing with the Yocto Project.
Note
要在镜像中安装包含调试符号和工具的调试包,请在 build/conf/local.conf 中添加 IMAGE_FEATURES += “dbg-pkgs tools-debug”。
To install debugging packages containing the debug symbols and tools in an image, add IMAGE_FEATURES += "dbg-pkgs tools-debug" in build/conf/local.conf.
使用 SDK 或安装了调试软件包和工具的映像,我们可以直接在目标机上调试应用程序,复制我们通常在本机上进行的开发工作流程。
Using the SDK or an image with the debugging packages and tools installed allows us to debug applications directly in the target, replicating the same development workflow we usually do on our machine.
由于内存或磁盘空间限制,GDB 可能无法在某些目标机上使用。造成这种限制的主要原因是,在启动调试程序之前,GDB 需要加载调试信息和调试程序的二进制文件。
The GDB may not be usable on some targets because of memory or disk space constraints. The main reason for this limitation is that the GDB needs to load the debugging information and the binaries of the debugging process before starting the debugging process.
为了克服这些限制,我们可以使用 gdbserver,它在使用 IMAGE_FEATURES 中的 tools-debug 时默认包含。它在目标机上运行,不会从调试进程中加载任何调试信息。相反,GDB 实例会在编译主机上处理调试信息。主机 GDB 会向 gdbserver 发送控制命令,以控制调试程序,因此目标机无需安装调试符号。
To overcome these constraints, we can use gdbserver, included by default when using tools-debug in IMAGE_FEATURES. It runs on the target and doesn’t load any debugging information from the debugged process. Instead, a GDB instance processes the debugging information on the build host. The host GDB sends control commands to gdbserver to control the debugged application, so the target does not need to have the debugging symbols installed.
不过,我们必须确保主机能够访问二进制文件及其调试信息。因此,建议在编译目标二进制文件时不做任何优化,以方便调试过程。
However, we must ensure the host can access the binaries with their debugging information. Therefore, it is recommended that the target binaries are compiled with no optimization to facilitate the debugging process.
8, Summary
在本章中,我们学习了如何配置 Poky 以帮助我们完成调试过程。我们了解了可用于调试的部署目录的内容,以及如何使用 buildhistory 跟踪我们的更改。我们还学习了使用 oe-pkgdata-util 检查软件包信息、使用 bitbake-getvar 调试变量扩展、如何使用 devshell 模拟与 BitBake 相同的编译环境,以及如何配置系统以提供 GDB 调试所需的工具。
In this chapter, we learned how to configure Poky to help us with the debugging process. We learned about the contents of deployed directories that can be used for debugging and how we can track our changes using buildhistory. We also covered the use of oe-pkgdata-util to inspect package information, use bitbake-getvar to debug variable expansion, how we can use devshell to emulate the same build environment found by BitBake, and how we configure our system to provide the tools needed for GDB debugging.
在下一章中,我们将学习如何使用外部分层来扩展 Poky 源代码。首先,我们将介绍分层的概念。然后,我们将详细了解每种层类型的目录结构和内容。
In the next chapter, we will learn how to expand the Poky source code using external layers. First, we will introduce the concept of layering. Then, we will learn in detail about the directory structure and the content of each layer type.