在answer on a sister site中,我试图从Linux内核数组unix_socket_table@net/unix/af_unix.c
转储信息,该信息定义为:
struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE];
目前,我在stp
脚本中对数组的大小进行了硬编码:
for (i = 0; i < 512; i++)
我怎么能避免这种情况?该信息(数组的大小)存储在调试信息中。 gdb
可以告诉我:
$ gdb --batch --ex 'whatis unix_socket_table' "/usr/lib/debug/boot/vmlinux-$(uname -r)"
type = struct hlist_head [512]
$ gdb --batch --ex 'p sizeof(unix_socket_table)/sizeof(*unix_socket_table)' "/usr/lib/debug/boot/vmlinux-$(uname -r)"
$1 = 512
但是如何在systemtap
中完成? AFAICT,systemtap没有sizeof()
运算符。
答案 0 :(得分:5)
如果是类型,可以使用@cast
运算符:
size=&@cast(0,"$TYPENAME")[1]
但是,unix_socket_table
不是一种类型。因此,计划B,对变量使用symdata
(在附近的任何旧内核函数的范围内)。
probe begin /* kernel.function("*@net/unix/af_unix.c") */ {
println(symdata(& @var("unix_socket_table")))
exit()
}
结果在这里:
unix_socket_table+0x0/0x1000 [kernel]
第二个十六进制数是符号大小,在脚本处理时从ELF符号表计算,相当于此处的4096数字:
% readelf -s /usr/lib/debug/lib/modules/`uname -r`/vmlinux | grep unix_socket_table
71901: ffffffff82023dc0 4096 OBJECT GLOBAL DEFAULT 28 unix_socket_table
您可以使用例如:
获取号码probe begin {
tokenize(symdata(@var("unix_socket_table@net/unix/af_unix.c")),"/");
printf("%d\n", strtol(tokenize("",""), 16));
exit()
}
答案 1 :(得分:3)
非常感谢@fche for pointing me in the right direction。正如他所说,systemtap
的{{1}}函数可用于检索给定地址(包括大小)的符号信息。因此,我们可以编写自己的symdata()
函数来解析它,将大小提取为:
sizeof()
如果我们看一下function sizeof(address:long) {
tokenize(symdata(address), "/");
return strtol(tokenize("",""),16);
}
函数的定义,我们会发现它本身就是一个symdata()
函数,它使用systemtap
C函数,它本身调用_stp_snprint_addr()
检索数据。这意味着我们也可以直接使用_stp_kallsyms_lookup()
定义我们自己的sizeof()
:
stp_kallsyms_lookup()
(请注意,我们需要function sizeof:long (addr:long) %{ /* pure */ /* pragma:symbols */
STAP_RETVALUE = -1;
_stp_kallsyms_lookup(STAP_ARG_addr, (unsigned long*)&(STAP_RETVALUE), NULL, NULL, NULL);
%}
( guru ),因为我们正在使用嵌入式C)。
现在,为了获得数组大小,我们需要数组元素的大小。一种方法可以是使用阵列的2个元素之间的地址偏移。因此,我们可以将-g
函数定义为:
array_size()
(其中function array_size(first:long, second:long) {
return sizeof(first) / (second - first);
}
是上面定义的一个或另一个函数)。
并将其命名为:
sizeof()
这给了我们probe begin {
printf("%d\n", array_size(
&@var("unix_socket_table@net/unix/af_unix.c")[0],
&@var("unix_socket_table@net/unix/af_unix.c")[1]));
exit();
}
预期的效果。
对于512
,另一种方法可能是使用C sizeof()
运算符:
sizeof()
(也需要$ sudo stap -ge '
%{ #include <net/af_unix.h> %}
probe begin {
printf("%d\n", %{ sizeof(unix_socket_table)/sizeof(unix_socket_table[0]) %} );
exit();
}'
512
)但是然后从内核源代码(头文件)中检索信息,而不是调试信息,所以虽然这对于头文件中定义的内核数组有用,但是这种方法赢了所有阵列的必要工作。