S390 调试特性

files
  • arch/s390/kernel/debug.c

  • arch/s390/include/asm/debug.h

描述:

此特性的目标是提供一个内核调试日志记录 API,使日志记录能够高效地存储在内存中,并且每个组件(例如设备驱动程序)都可以拥有一个单独的调试日志。其目的之一是在生产系统崩溃后检查调试日志,以分析崩溃原因。

如果系统仍在运行,但只有使用 dbf 的某个子组件失效,则可以通过 Linux debugfs 文件系统在运行中的系统上查看调试日志。

该调试特性对于内核和驱动程序开发也非常有用。

设计:

内核组件(例如设备驱动程序)可以通过函数调用 debug_register() 向调试特性注册自己。此函数为调用者初始化一个调试日志。对于每个调试日志,存在多个调试区域,其中在同一时间确切只有一个是活动的。每个调试区域由内存中的连续页面组成。调试区域中存储了由事件和异常调用写入的调试条目(日志记录)。

事件调用将指定的调试条目写入活动的调试区域,并更新活动区域的日志指针。如果到达了活动调试区域的末尾,则会进行回绕(环形缓冲区),下一个调试条目将写入活动调试区域的开头。

异常调用将指定的调试条目写入日志,并切换到下一个调试区域。这样做是为了确保在当前区域发生回绕时,描述异常根源的记录不会被覆盖。

调试区域本身也按环形缓冲区的形式排序。当在最后一个调试区域中抛出异常时,随后的调试条目将再次写入到最开始的第一个区域中。

事件和异常调用有四个版本:一个用于记录原始数据,一个用于文本,一个用于数字(unsigned int 和 long),以及一个用于类似 sprintf 格式的字符串。

每个调试条目包含以下数据

  • 时间戳

  • 调用任务的 CPU 编号

  • 调试条目的级别 (0...6)

  • 调用者的返回地址

  • 标志,指示条目是否为异常

可以通过 debugfs 文件系统中的条目在运行中的系统上检查调试日志。在顶级目录“s390dbf”下,每个注册的组件都有一个目录,其命名与对应的组件相同。debugfs 通常应挂载到 /sys/kernel/debug,因此可以通过 /sys/kernel/debug/s390dbf 访问调试特性。

这些目录的内容是代表调试日志不同视图的文件。每个组件可以通过使用函数 debug_register_view() 注册它们来决定使用哪些视图。系统提供了针对 hex/ascii 和 sprintf 数据的预定义视图。也可以定义其他视图。只需读取相应的 debugfs 文件即可检查视图的内容。

所有调试日志都有一个当前的调试级别(范围从 0 到 6)。默认级别为 3。事件和异常函数都有一个 level 参数。只有级别低于或等于当前级别的调试条目才会被写入日志。这意味着,在写入事件时,高优先级的日志条目应该具有较低的级别值,而低优先级的条目应该具有较高的级别值。借助 debugfs 文件系统,可以通过向为每个调试日志提供的 level debugfs 文件写入数字字符串“x”来更改当前的调试级别。通过在 level debugfs 文件上使用“-”,可以完全关闭调试功能。

示例

> echo "-" > /sys/kernel/debug/s390dbf/dasd/level

也可以为所有调试日志全局停用调试特性。您可以使用 /proc/sys/s390dbf 中的 2 个 sysctl 参数来更改此行为

目前有 2 个可能触发全局停止调试特性的触发器。第一种可能性是使用 debug_active sysctl。如果设置为 1,则调试特性正在运行。如果 debug_active 设置为 0,则关闭调试特性。

停止调试特性的第二个触发器是内核 oops。这可以防止调试特性覆盖 oops 发生之前发生的调试信息。在发生 oops 后,您可以通过向 /proc/sys/s390dbf/debug_active 写入 1 来重新激活调试特性。不过,不建议在生产环境中使用发生过 oops 的内核。

如果您想禁止停用调试特性,可以使用 debug_stoppable sysctl。如果将 debug_stoppable 设置为 0,则调试特性无法停止。如果调试特性已经停止,它将保持停用状态。

内核参数

可以使用 s390dbf 内核参数在启动早期配置调试日志的大小和日志级别。该参数接受调试日志名称、日志级别和日志大小,并用冒号字符(:)分隔。若只需配置单个属性,可以省略日志级别或日志大小之一。

要配置多个调试日志,可以多次指定该参数,或者在单个实例中提供多组参数,并用逗号分隔。

参数格式

s390dbf=<name|pattern>:[<level>|-]:[<pages>][,...]

其中

  • name 指定确切的调试日志名称或 shell 风格的通配符模式

  • level 指定日志级别,或者使用 - 完全停用该日志

  • pages 以页面数指定调试区域大小

示例

s390dbf=cio*:6:128,sclp_err::2

此示例将名称以 cio 开头的所有调试日志的日志级别设置为 6,日志大小设置为 128 页。它还将 sclp_err 调试日志的日志级别设置为 2。

内核接口:

bool debug_next_entry(file_private_info_t *p_info)

转到下一个条目

参数

file_private_info_t *p_info

被操作的私有信息

描述

p_info 中的当前位置设置为下一个条目。如果不存在更多的条目,则将当前位置设置为末尾之后的一个位置,并且返回值指示不存在更多条目。

返回

如果后面还有更多条目则为真,否则为假

void debug_to_act_entry(file_private_info_t *p_info)

转到当前活动的条目

参数

file_private_info_t *p_info

被操作的私有信息

描述

p_info 中的当前位置设置为 p_info->debug_info_snap 的当前活动条目

bool debug_prev_entry(file_private_info_t *p_info)

转到上一个条目

参数

file_private_info_t *p_info

被操作的私有信息

描述

p_info 中的当前位置设置为上一个条目。如果不存在上一个条目,则将当前位置保持为 DEBUG_PROLOG_ENTRY,并且返回值指示不存在上一个条目。

返回

如果前面还有更多条目则为真,否则为假

bool debug_move_entry(file_private_info_t *p_info, bool reverse)

沿前进或后退方向转到下一个条目

参数

file_private_info_t *p_info

被操作的私有信息

bool reverse

如果为 true,则按相反方向(即上一个)转到下一个条目

描述

p_info 中的当前位置设置为下一个(reverse == false)或上一个(reverse == true)条目。

返回

如果该方向上还有更多条目则为真,否则为假。

ssize_t debug_dump(debug_info_t *id, struct debug_view *view, char *buf, size_t buf_size, bool reverse)

获取调试信息的文本表示,或获取能容纳的尽可能多的内容

参数

debug_info_t *id

要使用的调试信息

struct debug_view *view

用于转储调试信息的视图

char *buf

写入文本调试数据表示的缓冲区

size_t buf_size

缓冲区的大小,包括末尾的 ‘0’ 字节

bool reverse

从最后写入的条目开始向后倒退

描述

每当需要调试信息的文本表示而又不想使用 s390dbf 文件时,都可以使用此函数。

注意

调用者有责任提供与调试信息数据兼容的视图。

返回

成功时返回写入缓冲区的字节数(不包括末尾的 ‘0’ 字节)。如果 buf_size == 0,则函数返回 0。失败时返回小于 0 的错误代码。

debug_info_t *debug_register_mode(const char *name, int pages_per_area, int nr_areas, int buf_size, umode_t mode, uid_t uid, gid_t gid)

创建并初始化调试区域。

参数

const char *name

调试日志的名称(例如用于 debugfs 条目)

int pages_per_area

每个区域将分配的页面数

int nr_areas

调试区域的数量

int buf_size

每个调试条目中数据区域的大小

umode_t mode

debugfs 文件的文件模式。例如 S_IRWXUGO

uid_t uid

debugfs 文件的用户 ID。目前仅支持 0。

gid_t gid

debugfs 文件的组 ID。目前仅支持 0。

返回

  • 生成调试区域的句柄

  • 如果注册失败则返回 NULL

描述

为调试日志分配内存。不能在中断处理程序中调用。

debug_info_t *debug_register(const char *name, int pages_per_area, int nr_areas, int buf_size)

使用默认文件模式创建并初始化调试区域。

参数

const char *name

调试日志的名称(例如用于 debugfs 条目)

int pages_per_area

每个区域将分配的页面数

int nr_areas

调试区域的数量

int buf_size

每个调试条目中数据区域的大小

返回

  • 生成调试区域的句柄

  • 如果注册失败则返回 NULL

描述

为调试日志分配内存。debugfs 文件模式访问权限为用户的读和写。不能在中断处理程序中调用。

void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)

注册一个静态调试区域

参数

debug_info_t *id

静态调试区域的句柄

int pages_per_area

每个区域的页面数

int nr_areas

调试区域的数量

描述

注册使用 DEFINE_STATIC_DEBUG_INFO 定义的 debug_info_t。

注意

此函数通过以下方式自动调用:由

DEFINE_STATIC_DEBUG_INFO 生成的 initcall。

void debug_unregister(debug_info_t *id)

归还调试区域。

参数

debug_info_t *id

调试日志的句柄

返回

void debug_set_level(debug_info_t *id, int new_level)

如果 new_level 有效,则设置新的当前调试级别。

参数

debug_info_t *id

调试日志的句柄

int new_level

新的调试级别

返回

void debug_stop_all(void)

如果允许停止,则停止调试特性。

参数

void

无参数

返回

描述

当前用于发生内核 oops 的情况。

void debug_set_critical(void)

事件/异常函数尝试使用锁而不是自旋。

参数

void

无参数

返回

描述

当前用于停止除当前 CPU 之外的所有 CPU 的情况。一旦处于此状态,用于为事件或异常写入调试条目的函数将不再在调试区域锁上自旋,而只是尝试获取它,如果获取不到锁则失败。

int debug_register_view(debug_info_t *id, struct debug_view *view)

注册新的调试视图并创建 debugfs 目录条目

参数

debug_info_t *id

调试日志的句柄

struct debug_view *view

指向调试视图结构体的指针

返回

  • 0 : 成功

  • < 0: 错误

int debug_unregister_view(debug_info_t *id, struct debug_view *view)

注销调试视图并删除 debugfs 目录条目

参数

debug_info_t *id

调试日志的句柄

struct debug_view *view

指向调试视图结构体的指针

返回

  • 0 : 成功

  • < 0: 错误

bool debug_level_enabled(debug_info_t *id, int level)

如果将记录指定级别的调试事件,则返回 true。否则返回 false。

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

返回

  • 如果级别小于或等于当前调试级别,则为 true

debug_entry_t *debug_event(debug_info_t *id, int level, void *data, int length)

将二进制调试条目写入活动的调试区域(如果级别 <= 当前调试级别)

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

void *data

指向调试条目数据的指针

int length

数据的长度(以字节为单位)

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_entry_t *debug_int_event(debug_info_t *id, int level, unsigned int tag)

将无符号整数调试条目写入活动的调试区域(如果级别 <= 当前调试级别)

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

unsigned int tag

调试条目的整数值

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_entry_t *debug_long_event(debug_info_t *id, int level, unsigned long tag)

将无符号长整数调试条目写入活动的调试区域(如果级别 <= 当前调试级别)

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

unsigned long tag

调试条目的长整数值

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_entry_t *debug_text_event(debug_info_t *id, int level, const char *txt)

以 ascii 格式将字符串调试条目写入活动的调试区域(如果级别 <= 当前调试级别)

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

const char *txt

调试条目的字符串

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_sprintf_event

debug_sprintf_event (_id, _level, _fmt, ...)

将带有格式化字符串和变长参数(long)的调试条目写入活动的调试区域(如果级别 <= 当前调试级别)。

参数

_id

调试日志的句柄

_level

调试级别

_fmt

调试条目的格式化字符串

...

变长参数的使用方式与 sprintf() 相同

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

描述

不能将浮点数(float)和长长整型(long long)数据类型用作变长参数。

debug_entry_t *debug_exception(debug_info_t *id, int level, void *data, int length)

将二进制调试条目写入活动的调试区域(如果级别 <= 当前调试级别)并切换到下一个调试区域

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

void *data

指向调试条目数据的指针

int length

数据的长度(以字节为单位)

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_entry_t *debug_int_exception(debug_info_t *id, int level, unsigned int tag)

将无符号整型调试条目写入活动的调试区域(如果级别 <= 当前调试级别)并切换到下一个调试区域

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

unsigned int tag

调试条目的整数值

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_entry_t *debug_long_exception(debug_info_t *id, int level, unsigned long tag)

将长整型调试条目写入活动的调试区域(如果级别 <= 当前调试级别)并切换到下一个调试区域

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

unsigned long tag

调试条目的长整数值

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_entry_t *debug_text_exception(debug_info_t *id, int level, const char *txt)

以 ascii 格式将字符串调试条目写入活动的调试区域(如果级别 <= 当前调试级别)并切换到下一个调试区域

参数

debug_info_t *id

调试日志的句柄

int level

调试级别

const char *txt

调试条目的字符串

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

debug_sprintf_exception

debug_sprintf_exception (_id, _level, _fmt, ...)

将带有格式化字符串和变长参数(long)的调试条目写入活动的调试区域(如果级别 <= 当前调试级别)并切换到下一个调试区域。

参数

_id

调试日志的句柄

_level

调试级别

_fmt

调试条目的格式化字符串

...

变长参数的使用方式与 sprintf() 相同

返回

  • 写入的调试条目的地址

  • 如果出错则为 NULL

描述

不能将浮点数(float)和长长整型(long long)数据类型用作变长参数。

DEFINE_STATIC_DEBUG_INFO

DEFINE_STATIC_DEBUG_INFO (var, name, pages, nr_areas, buf_size, view)

定义静态的 debug_info_t

参数

var

debug_info_t 变量的名称

name

调试日志的名称(例如用于 debugfs 条目)

pages

每个区域的页面数

nr_areas

调试区域的数量

buf_size

每个调试条目中数据区域的大小

view

指向调试视图结构体的指针

描述

为早期跟踪定义一个静态的 debug_info_t。关联的 debugfs 日志会自动使用指定的调试视图进行注册。

重要提示:此宏的使用者绝不能针对此 debug_info_t 调用任何 debug_register/_unregister() 函数!

注意

跟踪将从固定数量的初始页面和区域开始。调试区域将在 arch_initcall 期间更改为使用指定数量。

预定义视图:

extern struct debug_view debug_hex_ascii_view;

extern struct debug_view debug_sprintf_view;

示例

/*
 * hex_ascii-view Example
 */

#include <linux/init.h>
#include <asm/debug.h>

static debug_info_t *debug_info;

static int init(void)
{
    /* register 4 debug areas with one page each and 4 byte data field */

    debug_info = debug_register("test", 1, 4, 4 );
    debug_register_view(debug_info, &debug_hex_ascii_view);

    debug_text_event(debug_info, 4 , "one ");
    debug_int_exception(debug_info, 4, 4711);
    debug_event(debug_info, 3, &debug_info, 4);

    return 0;
}

static void cleanup(void)
{
    debug_unregister(debug_info);
}

module_init(init);
module_exit(cleanup);
/*
 * sprintf-view Example
 */

#include <linux/init.h>
#include <asm/debug.h>

static debug_info_t *debug_info;

static int init(void)
{
    /* register 4 debug areas with one page each and data field for */
    /* format string pointer + 2 varargs (= 3 * sizeof(long))       */

    debug_info = debug_register("test", 1, 4, sizeof(long) * 3);
    debug_register_view(debug_info, &debug_sprintf_view);

    debug_sprintf_event(debug_info, 2 , "first event in %s:%i\n",__FILE__,__LINE__);
    debug_sprintf_exception(debug_info, 1, "pointer to debug info: %p\n",&debug_info);

    return 0;
}

static void cleanup(void)
{
    debug_unregister(debug_info);
}

module_init(init);
module_exit(cleanup);

Debugfs 接口

可以通过读取相应的 debugfs 文件来查看调试日志的视图

示例

> ls /sys/kernel/debug/s390dbf/dasd
flush  hex_ascii  level pages
> cat /sys/kernel/debug/s390dbf/dasd/hex_ascii | sort -k2,2 -s
00 00974733272:680099 2 - 02 0006ad7e  07 ea 4a 90 | ....
00 00974733272:682210 2 - 02 0006ade6  46 52 45 45 | FREE
00 00974733272:682213 2 - 02 0006adf6  07 ea 4a 90 | ....
00 00974733272:682281 1 * 02 0006ab08  41 4c 4c 43 | EXCP
01 00974733272:682284 2 - 02 0006ab16  45 43 4b 44 | ECKD
01 00974733272:682287 2 - 02 0006ab28  00 00 00 04 | ....
01 00974733272:682289 2 - 02 0006ab3e  00 00 00 20 | ...
01 00974733272:682297 2 - 02 0006ad7e  07 ea 4a 90 | ....
01 00974733272:684384 2 - 00 0006ade6  46 52 45 45 | FREE
01 00974733272:684388 2 - 00 0006adf6  07 ea 4a 90 | ....

有关上述输出的解释,请参阅关于预定义视图的章节!

更改调试级别

示例

> cat /sys/kernel/debug/s390dbf/dasd/level
3
> echo "5" > /sys/kernel/debug/s390dbf/dasd/level
> cat /sys/kernel/debug/s390dbf/dasd/level
5

刷新调试区域

可以通过将所需区域的编号(0...n)通过管道输入到“flush” debugfs 文件来刷新调试区域。当使用“-”时,将刷新所有调试区域。

示例

  1. 刷新调试区域 0

    > echo "0" > /sys/kernel/debug/s390dbf/dasd/flush
    
  2. 刷新所有调试区域

    > echo "-" > /sys/kernel/debug/s390dbf/dasd/flush
    

更改调试区域的大小

要调整调试区域的大小,请将所需的页面数写入“pages”文件。如果现有数据能容纳,则会保留;否则,最旧的条目将被丢弃。

示例

为调试特性“dasd”的调试区域定义 4 个页面

> echo "4" > /sys/kernel/debug/s390dbf/dasd/pages

停止调试特性

示例

  1. 检查是否允许停止

    > cat /proc/sys/s390dbf/debug_stoppable
    
  2. 停止调试特性

    > echo 0 > /proc/sys/s390dbf/debug_active
    

crash 接口

自 v5.1.0 起,crash 工具内置了 s390dbf 命令,用于显示所有调试日志或将它们导出到文件系统。使用此工具,可以在运行中的系统上以及系统崩溃后的内存转储中检查调试日志。

检查原始内存

在运行中的系统上以及系统崩溃后检查调试日志的最后一种可能性是查看 VM 下或服务元素(Service Element)处的原始内存。可以通过系统映射(System map)中的 debug_area_first 符号找到调试日志的锚点。然后必须顺着 debug.h 中定义的数据结构的正确指针,在内存中找到调试区域。通常使用调试特性的模块也会有一个指向调试日志指针的全局变量。顺着这个指针,也可以在内存中找到调试日志。

对于此方法,建议在 debug_register() 中将数据字段的长度设为 ‘16 * x + 4’ 字节(x = 0..n),以便能够良好地格式化显示调试条目。

预定义视图

有两个预定义视图:hex_ascii 和 sprintf。hex_ascii 视图以十六进制和 ascii 表示形式显示数据字段(例如 45 43 4b 44 | ECKD)。

sprintf 视图以 sprintf 函数相同的方式格式化调试条目。sprintf 事件/异常函数向调试条目写入一个指向格式化字符串的指针(大小 = sizeof(long)),并为每个变长参数写入一个 long 值。因此,例如对于带有格式化字符串外加两个变长参数的调试条目,需要在 debug_register() 函数中分配一个 (3 * sizeof(long)) 字节的数据区域。

重要事项

在 sprintf 事件函数中使用“%s”是危险的。只有当传递的字符串的内存只要调试特性存在就一直可用时,才能在 sprintf 事件函数中使用“%s”。背后的原因是,出于性能考虑,调试特性中只存储指向字符串的指针。如果您记录了一个随后被释放的字符串,当检查调试特性时将会触发 OOPS,因为此时调试特性会访问已经释放的内存。

注意

如果使用 sprintf 视图,切勿使用 sprintf 事件和异常函数以外的其他事件/异常函数。

hex_ascii 和 sprintf 视图的格式如下

  • 区域编号

  • 时间戳(格式化为自 1970 年 1 月 1 日 00:00:00 协调世界时 (UTC) 以来的秒数和微秒数)

  • 调试条目的级别

  • 异常标志(* = 异常)

  • 调用任务的 CPU 编号

  • 调用者的返回地址

  • 数据字段

hex_ascii 视图的典型行将如下所示(第一行仅用于解释,在 ‘cat’ 视图时不会显示)

area  time           level exception cpu caller    data (hex + ascii)
--------------------------------------------------------------------------
00    00964419409:440690 1 -         00  88023fe

定义视图

视图使用 ‘debug_view’ 结构体指定。其中定义了用于读取和写入 debugfs 文件的回调函数

struct debug_view {
      char name[DEBUG_MAX_PROCF_LEN];
      debug_prolog_proc_t* prolog_proc;
      debug_header_proc_t* header_proc;
      debug_format_proc_t* format_proc;
      debug_input_proc_t*  input_proc;
      void*                private_data;
};

其中

typedef int (debug_header_proc_t) (debug_info_t* id,
                                   struct debug_view* view,
                                   int area,
                                   debug_entry_t* entry,
                                   char* out_buf);

typedef int (debug_format_proc_t) (debug_info_t* id,
                                   struct debug_view* view, char* out_buf,
                                   const char* in_buf);
typedef int (debug_prolog_proc_t) (debug_info_t* id,
                                   struct debug_view* view,
                                   char* out_buf);
typedef int (debug_input_proc_t) (debug_info_t* id,
                                  struct debug_view* view,
                                  struct file* file, const char* user_buf,
                                  size_t in_buf_size, loff_t* offset);

“private_data” 成员可用作指向视图特定数据的指针。调试特性本身不使用它。

读取 debugfs 文件时的输出结构如下

"prolog_proc output"

"header_proc output 1"  "format_proc output 1"
"header_proc output 2"  "format_proc output 2"
"header_proc output 3"  "format_proc output 3"
...

从 debugfs 读取视图时,调试特性会调用一次 ‘prolog_proc’ 来写入序言(prolog)。然后针对每个现有的调试条目调用 ‘header_proc’ 和 ‘format_proc’。

当向视图写入内容时,可以使用 input_proc 来实现相应的功能(例如使用 echo "0" > /sys/kernel/debug/s390dbf/dasd/level )。

对于 header_proc,可以使用定义在 debug.h 中的默认函数 debug_dflt_header_fn(),它会产生与预定义视图相同的头部输出。例如

00 00964419409:440761 2 - 00 88023ec

要了解如何使用回调函数,请检查默认视图的实现!

示例

#include <asm/debug.h>

#define UNKNOWNSTR "data: %08x"

const char* messages[] =
{"This error...........\n",
 "That error...........\n",
 "Problem..............\n",
 "Something went wrong.\n",
 "Everything ok........\n",
 NULL
};

static int debug_test_format_fn(
   debug_info_t *id, struct debug_view *view,
   char *out_buf, const char *in_buf
)
{
  int i, rc = 0;

  if (id->buf_size >= 4) {
     int msg_nr = *((int*)in_buf);
     if (msg_nr < sizeof(messages) / sizeof(char*) - 1)
        rc += sprintf(out_buf, "%s", messages[msg_nr]);
     else
        rc += sprintf(out_buf, UNKNOWNSTR, msg_nr);
  }
  return rc;
}

struct debug_view debug_test_view = {
  "myview",                 /* name of view */
  NULL,                     /* no prolog */
  &debug_dflt_header_fn,    /* default header for each entry */
  &debug_test_format_fn,    /* our own format function */
  NULL,                     /* no input function */
  NULL                      /* no private data */
};

测试:

debug_info_t *debug_info;
int i;
...
debug_info = debug_register("test", 0, 4, 4);
debug_register_view(debug_info, &debug_test_view);
for (i = 0; i < 10; i ++)
  debug_int_event(debug_info, 1, i);
> cat /sys/kernel/debug/s390dbf/test/myview
00 00964419734:611402 1 - 00 88042ca   This error...........
00 00964419734:611405 1 - 00 88042ca   That error...........
00 00964419734:611408 1 - 00 88042ca   Problem..............
00 00964419734:611411 1 - 00 88042ca   Something went wrong.
00 00964419734:611414 1 - 00 88042ca   Everything ok........
00 00964419734:611417 1 - 00 88042ca   data: 00000005
00 00964419734:611419 1 - 00 88042ca   data: 00000006
00 00964419734:611422 1 - 00 88042ca   data: 00000007
00 00964419734:611425 1 - 00 88042ca   data: 00000008
00 00964419734:611428 1 - 00 88042ca   data: 00000009