2015-03-13 2 views
1

В одном answer on a sister site, я пытаюсь сбросить информацию из массива [email protected]/unix/af_unix.c ядра Linux, которая определяется как:получить размер целевого массива в SystemTap

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().

ответ

3

Огромное спасибо @fche for pointing me in the right direction. По его словам, функция 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 (гуру) здесь, как мы используем встроенный C).

Теперь, чтобы получить размер массива, нам нужен размер элементов массивов. Одним из подходов может быть использование смещения адреса между двумя элементами массива. Таким образом, мы могли бы определить нашу array_size() функцию:

function array_size(first:long, second:long) { 
    return sizeof(first)/(second - first); 
} 

(где sizeof() это один или другой из функций, определенных выше).

И называют его:

probe begin { 
    printf("%d\n", array_size(
    &@var("[email protected]/unix/af_unix.c")[0], 
    &@var("[email protected]/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), но тогда информация извлекается из исходного кода ядра (файлы заголовков), не отладить информацию, поэтому, хотя это будет работать для массивов ядер, которые определены в файлах заголовков, этот подход не будет работать для всех массивов.

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("[email protected]/unix/af_unix.c")),"/"); 
    printf("%d\n", strtol(tokenize("",""), 16)); 
    exit() 
}