2

一个姊妹网站的回答中,我试图从unix_socket_table@net/unix/af_unix.c定义为的 Linux 内核数组中转储信息:

struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE];

目前,我在stp脚本中硬编码数组的大小:

对于 (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()操作员。

4

2 回答 2

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()
}
于 2015-03-14T01:41:43.277 回答
3

非常感谢@fche 为我指明了正确的方向。正如他所说,systemtapsymdata()函数可用于在给定地址检索符号信息,包括大小。所以我们可以编写自己的sizeof()函数来解析它以提取大小为:

function sizeof(address:long) {
  tokenize(symdata(address), "/");
  return strtol(tokenize("",""),16);
}

如果我们查看该symdata()函数的定义,我们会发现它本身就是一个systemtap使用_stp_snprint_addr()C 函数的函数,它本身调用_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);
%}

(请注意,我们需要-g( guru ),因为我们使用的是嵌入式 C)。

现在,要获得数组大小,我们需要数组元素的大小。一种方法是使用数组的 2 个元素之间的地址偏移量。所以我们可以将我们的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预期的结果。

对于sizeof(),另一种方法是使用 Csizeof()运算符:

$ sudo stap -ge '
%{ #include <net/af_unix.h> %}
probe begin {
  printf("%d\n", %{ sizeof(unix_socket_table)/sizeof(unix_socket_table[0]) %} );
  exit();
}'
512

(也需要-g)但随后信息是从内核源代码(头文件)中检索的,而不是调试信息,因此虽然这适用于在头文件中定义的内核数组,但该方法不一定适用于所有数组。

于 2015-03-13T14:41:58.230 回答