开发 gem5 模型

DebugFlags:gem5 中的调试和日志记录

重要提示:本幻灯片基于 SimObjects 简介 中已开发的内容。


DebugFlags

DebugFlags 有助于调试打印。调试打印对于在 gem5 中调试模型和记录日志非常有用。

每个 DebugFlag 可以启用 gem5 代码库中某些语句的打印。运行以下命令以查看 gem5 中所有可用的 DebugFlags

cd gem5
./build/NULL/gem5.opt --debug-help

此命令将显示所有 DebugFlags 的列表。您可以选择使用特定的 DebugFlag,例如 Activity,或者选择一类 DebugFlags,例如 Registers,这将启用以下 DebugFlagsIntRegsFloatRegsVecRegsVecPredRegsMatRegsCCRegsMiscRegs

在下一张幻灯片中,您将看到预期的输出。



步骤 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 来访问标志。您可以通过调用 enabledisable 方法来启用和禁用标志。下面是一个示例,说明如果您想启用 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 开发时使用 assertstatic_assert。它们将帮助您发现您做出的错误假设,并帮助您及早发现任何开发错误。assertstatic_assert 是标准的 C++ 函数,您可以在 gem5 开发中使用(强烈建议使用)。

fatalfatal_ifpanicpanic_if 是 gem5 特定的类似断言函数,允许您打印错误消息。gem5 的约定是使用 fatalfatal_if 来断言用户输入的假设(类似于 ValueError)。例如,如果用户尝试使用负容量配置您的 SimObject,您可以在 SimObject 中使用 fatalfatal_if 来让用户(很可能是您自己)知道他们的错误。下面显示了使用 fatalfatal_if 执行此操作的示例。

if (capacity < 0) { fatal("capacity can not be negative.\n"); }
\\ OR
fatal_if(capacity < 0, "capacity can not be negative.\n");

您应该使用 panicpanic_if 来捕获开发者的错误。我们将在 Ports 中看到一些示例。


gem5 中的其他调试设施

DPRINTF(Flag, __VA_ARGS__)
DPRINTFR(Flag, __VA_ARGS__)

gem5 中的其他调试设施

DPRINTFS(Flag, SimObject, __VA_ARGS__)
DPRINTFN(__VA_ARGS__)
DPRINTFNR(__VA_ARGS__)
DDUMP(Flag, data, count)

了解更多信息:https://www.gem5.org/documentation/learning_gem5/part2/debugging/