получить размер целевого массива в systemtap

В ответе на дочернем сайте я пытаюсь вывести информацию из массива ядра 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().


person Stephane Chazelas    schedule 13.03.2015    source источник


Ответы (2)


Если бы это был тип, можно было бы использовать оператор @cast:

size=&@cast(0,"$TYPENAME")[1]

но, увы, unix_socket_table не тип. Итак, план Б, используйте 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()
}
person fche    schedule 14.03.2015

Большое спасибо @fche за то, что указал мне правильное направление. По его словам, функция symdata() systemtap может использоваться для получения информации о символе по заданному адресу, включая размер. Таким образом, мы можем написать нашу собственную функцию 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).

Теперь, чтобы получить размер массива, нам нужны размеры элементов массивов. Один из подходов может заключаться в использовании смещения адреса между двумя элементами массива. Таким образом, мы могли бы определить нашу функцию 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() другим подходом может быть использование оператора C 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

(также требуется -g), но тогда информация извлекается из исходного кода ядра (заголовочные файлы), а не из отладочной информации, поэтому, хотя это будет работать для массивов ядра, определенных в заголовочных файлах, этот подход не обязательно будет работать для всех массивов. .

person Stephane Chazelas    schedule 13.03.2015