DebugFlags:gem5 中的调试和日志记录
重要提示:本幻灯片基于 SimObjects 简介 中已开发的内容。
DebugFlags
DebugFlags 有助于调试打印。调试打印对于在 gem5 中调试模型和记录日志非常有用。
每个 DebugFlag 可以启用 gem5 代码库中某些语句的打印。运行以下命令以查看 gem5 中所有可用的 DebugFlags。
cd gem5
./build/NULL/gem5.opt --debug-help
此命令将显示所有 DebugFlags 的列表。您可以选择使用特定的 DebugFlag,例如 Activity,或者选择一类 DebugFlags,例如 Registers,这将启用以下 DebugFlags:IntRegs、FloatRegs、VecRegs、VecPredRegs、MatRegs、CCRegs、MiscRegs。
在下一张幻灯片中,您将看到预期的输出。
步骤 1:带 DebugFlags 的 SimObject
DebugFlags:HelloExampleFlag
要在 gem5 中定义新的 DebugFlag,您只需在 gem5 目录中的任何 SConscript 中定义它。但是,按照惯例,DebugFlags 应在注册与该 DebugFlag 相关的 SimObjects 的同一个 SConscript 中定义。
要定义一个新的 DebugFlag,我们将在 HelloSimObject 中使用它来打印调试/日志语句,请在您选择的编辑器中打开 src/bootcamp/hello-sim-object/SConscript 并添加以下行。
DebugFlag("HelloExampleFlag")
添加此行将创建一个新的自动生成的头文件(与 DebugFlag 同名),该文件在 C++ 中定义 DebugFlag。
DebugFlags:在代码中使用 HelloExampleFlag
gem5 中允许调试打印的函数之一是 DPRINTF,如果启用了某个 DebugFlag,它将允许您打印格式化的字符串(稍后将详细介绍如何启用 DebugFlags)。DPRINTF 定义在 src/base/trace.hh 中。每次要使用 DPRINTF 时,请确保包含它。
现在让我们实际在 C++ 中添加 HelloExampleFlag。正如我提到的,DebugFlags 的头文件是自动生成的。现在,请相信当我们重新编译 gem5 时,HelloExampleFlag 的头文件将在 build/NULL/debug/HelloExampleFlag.hh 中。
让我们通过在 hello_sim_object.cc 中添加以下行来包含头文件。请记住遵循包含文件的常规顺序!
#include "base/trace.hh"
#include "debug/HelloExampleFlag.hh
现在让我们在 HelloSimObject 的构造函数中添加一个简单的 DPRINTF 语句来打印 Hello from ...。通过在 for-loop 之后添加以下行来实现。注意:__func__ 将返回我们所在函数的名称作为字符串。
DPRINTF(HelloExampleFlag, "%s: Hello from HelloSimObject's constructor!\n", __func__);
DebugFlags:文件应该是什么样子
以下是更改后 src/bootcamp/hello-sim-object/SConscript 应该的样子。
Import("*")
SimObject("HelloSimObject.py", sim_objects=["HelloSimObject"])
Source("hello_sim_object.cc")
DebugFlag("HelloExampleFlag")
右侧是更改后 src/bootcamp/hello-sim-object/hello_sim_object.cc 应该的样子。
续
#include "bootcamp/hello-sim-object/hello_sim_object.hh"
#include <iostream>
#include "base/trace.hh"
#include "debug/HelloExampleFlag.hh"
namespace gem5
{
HelloSimObject::HelloSimObject(const HelloSimObjectParams& params):
SimObject(params)
{
for (int i = 0; i < params.num_hellos; i++) {
std::cout << "i: " << i << ", Hello from HelloSimObject's constructor!" << std::endl;
}
DPRINTF(HelloExampleFlag, "%s: Hello from HelloSimObject's constructor!\n", __func__);
}
} // namespace gem5
让我们重新编译
现在,让我们使用下面的命令重新编译 gem5。编译完成后,您应该能够在 build/NULL/debug/HelloExampleFlag.hh 中找到头文件。
scons build/NULL/gem5.opt -j$(nproc)
在下一张幻灯片中继续。
这里是 build/NULL/debug/HelloExampleFlag.hh 内容的片段。
/**
* DO NOT EDIT THIS FILE!
* File automatically generated by
* build_tools/debugflaghh.py:139
*/
#ifndef __DEBUG_HelloExampleFlag_HH__
#define __DEBUG_HelloExampleFlag_HH__
#include "base/compiler.hh" // For namespace deprecation
#include "base/debug.hh"
namespace gem5
{
namespace debug
{
namespace unions
{
inline union HelloExampleFlag
{
~HelloExampleFlag() {}
SimpleFlag HelloExampleFlag = {
"HelloExampleFlag", "", false
};
} HelloExampleFlag;
} // namespace unions
inline constexpr const auto& HelloExampleFlag =
::gem5::debug::unions::HelloExampleFlag.HelloExampleFlag;
} // namespace debug
} // namespace gem5
#endif // __DEBUG_HelloExampleFlag_HH__
DebugFlags:添加 HelloExampleFlag 之后
现在,每当我们从 gem5 打印调试帮助时,我们的 HelloExampleFlag 应该被列出。让我们在 gem5 基础目录中运行以下命令以验证我们的 DebugFlag 已添加。
./build/NULL/gem5.opt --debug-help
以下是预期的输出。
启用 DebugFlags:使用配置脚本
要启用 DebugFlag,您可以从 m5.debug 导入 flags,并通过索引 flags 来访问标志。您可以通过调用 enable 和 disable 方法来启用和禁用标志。下面是一个示例,说明如果您想启用 HelloExampleFlag,您的 second-hello-example.py 应该是什么样子。注意:现在不要在您的配置脚本中进行此更改。
import m5
from m5.debug import flags
from m5.objects.Root import Root
from m5.objects.HelloSimObject import HelloSimObject
root = Root(full_system=False)
root.hello = HelloSimObject(num_hellos=5)
m5.instantiate()
flags["HelloExampleFlag"].enable()
exit_event = m5.simulate()
print(f"Exited simulation because: {exit_event.getCause()}.")
启用 DebugFlags:使用命令行
或者,您可以在运行配置脚本时向 gem5 二进制文件传递 --debug-flags=[逗号分隔的 DebugFlags 列表]。例如,下面是一个 shell 命令,您可以使用它来启用 HelloExampleFlag(一如既往,在 gem5 基础目录中运行它)。
./build/NULL/gem5.opt --debug-flags=HelloExampleFlag configs/bootcamp/hello-sim-object/second-hello-example.py
模拟:不使用 HelloExampleFlag
现在让我们使用和不使用 DebugFlags 来模拟 second-hello-example.py,并比较输出。
运行以下命令以在不使用 DebugFlags 的情况下模拟 second-hello-example.py。
./build/NULL/gem5.opt configs/bootcamp/hello-sim-object/second-hello-example.py
以下是我执行此操作时终端的录制。
模拟:使用 HelloExampleFlag
运行以下命令以使用 HelloExampleFlag 模拟 second-hello-example.py。
./build/NULL/gem5.opt --debug-flags=HelloExampleFlag configs/bootcamp/hello-sim-object/second-hello-example.py
以下是我执行此操作时终端的录制。
步骤 1 结束
gem5 中的断言
我强烈建议在为 gem5 开发时使用 assert 和 static_assert。它们将帮助您发现您做出的错误假设,并帮助您及早发现任何开发错误。assert 和 static_assert 是标准的 C++ 函数,您可以在 gem5 开发中使用(强烈建议使用)。
fatal、fatal_if、panic 和 panic_if 是 gem5 特定的类似断言函数,允许您打印错误消息。gem5 的约定是使用 fatal 和 fatal_if 来断言用户输入的假设(类似于 ValueError)。例如,如果用户尝试使用负容量配置您的 SimObject,您可以在 SimObject 中使用 fatal 或 fatal_if 来让用户(很可能是您自己)知道他们的错误。下面显示了使用 fatal 和 fatal_if 执行此操作的示例。
if (capacity < 0) { fatal("capacity can not be negative.\n"); }
\\ OR
fatal_if(capacity < 0, "capacity can not be negative.\n");
您应该使用 panic 和 panic_if 来捕获开发者的错误。我们将在 Ports 中看到一些示例。
gem5 中的其他调试设施
- 大多数
DebugFlags要求当前作用域中存在name()函数(从SimObject成员函数调用)。 - 仅在使用
gem5.opt或gem5.debug时使用DebugFlags。
DPRINTF(Flag, __VA_ARGS__)
- 接受一个标志、一个格式字符串和格式参数。
- 仅在启用
Flag时打印格式化的字符串。
DPRINTFR(Flag, __VA_ARGS__)
- 输出调试语句而不打印名称。
- 对于在没有
name()函数的非SimObjects对象中使用调试语句很有用。
gem5 中的其他调试设施
DPRINTFS(Flag, SimObject, __VA_ARGS__)
- 对于从具有指向其所有者的指针的
SimObject的私有子类进行调试很有用。
DPRINTFN(__VA_ARGS__)
DPRINTFNR(__VA_ARGS__)
- 这些不接受标志作为参数,只要启用了调试就会始终打印。
DDUMP(Flag, data, count)
- 打印长度为
count字节的二进制data。 - 格式化为用户可读的十六进制。
了解更多信息:https://www.gem5.org/documentation/learning_gem5/part2/debugging/
