Профилируйте разделяемую библиотеку C, вызываемую программой Ruby

У меня есть программа, написанная на Ruby и C. Часть C — это разделяемая библиотека, которая является расширением для программы Ruby. Я хочу профилировать написанную мной совместно используемую библиотеку C с помощью gprof. Я компилирую общую библиотеку следующим образом:

gcc -I. -I/usr/lib/ruby/1.8/i486-linux -I/usr/lib/ruby/1.8/i486-linux -I. -D_FILE_OFFSET_BITS=64  -fPIC -fno-strict-aliasing -g -march=i686 -O2 -ggdb -pg -fPIC -c extension.c
gcc -shared -o extension.so extension.o -L. -L/usr/lib -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic  -lruby1.8  -lpthread -lrt -ldl -lcrypt -lm -lc

Затем я запускаю программу ruby, которая загружает эту общую библиотеку, и я ожидаю файл gmon.out в текущем каталоге, но по какой-то причине файл gmon.out не создается. Как мне это сделать?

Я искал это, но не смог найти удовлетворительного ответа (который сработал).

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

Я попытался написать программу на C, которая также напрямую использует разделяемую библиотеку. Я немедленно получаю ошибку страницы в одной из функций библиотеки ruby, которая вызывается во время инициализации общей библиотеки. Я думаю, что он действительно ожидает загрузки из рубиновой программы, которая может внутренне творить магию.

(gdb) bt
#0  0x0091556e in st_lookup () from /usr/lib/libruby1.8.so.1.8
#1  0x008e87c2 in rb_intern () from /usr/lib/libruby1.8.so.1.8
#2  0x008984a5 in rb_define_module () from /usr/lib/libruby1.8.so.1.8
#3  0x08048dd0 in Init_SimilarStr () at extension.c:542
#4  0x0804933e in main (argc=2, argv=0xbffff454) at extension.c:564

Обновление: неважно. Я использовал #ifdef для компиляции Ruby-частей расширения и получения профиля. Закрытие.


person Sudhanshu    schedule 12.01.2010    source источник
comment
В случае, если кто-то ищет ответ; Я думаю, что -pg также следует передать команде компоновщика; то есть gcc -shared -o extension.so extension.o -L. -L/usr/lib -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic -lruby1.8 -lpthread -lrt -ldl -lcrypt -lm -lc -pg   -  person dashesy    schedule 15.05.2012
comment
Я бы хотел, чтобы вы не закрывали этот вопрос только потому, что нашли ответ. Это полезно и другим людям.   -  person Juno Woods    schedule 26.09.2013


Ответы (3)


Я обнаружил, что в этой ситуации oprofile намного лучше подходит для профилирования, чем gprof. отчеты oprofile гораздо более полны. Я скомпилировал рубиновые части, которые вызывали seg-faults (не все из них были), из расширения C, используя #ifndef PROFILE, и заменил их не-рубиновым кодом. Я написал подпрограмму main() внутри самого расширения, чтобы вызывать функции в расширении. Затем я настроил make-файл для компиляции расширения как программы на C с определенным PROFILE. Затем я установил oprofile на Ubuntu< /а>. Написал этот скрипт.

#!/bin/bash
sudo opcontrol --reset
sudo opcontrol --start
./a.out Rome Damascus NewYork Delhi Bangalore
sudo opcontrol --shutdown
opreport -lt1

Скомпилировал мою программу и выполнил приведенный выше скрипт, который выдает такой вывод из команды «opreport»:

...
...
Killing daemon.
warning: /no-vmlinux could not be found.
warning: [vdso] (tgid:10675 range:0x920000-0x921000) could not be found.
warning: [vdso] (tgid:1270 range:0xba1000-0xba2000) could not be found.
warning: [vdso] (tgid:1675 range:0x973000-0x974000) could not be found.
warning: [vdso] (tgid:1711 range:0x264000-0x265000) could not be found.
warning: [vdso] (tgid:1737 range:0x990000-0x991000) could not be found.
warning: [vdso] (tgid:2477 range:0xa53000-0xa54000) could not be found.
warning: [vdso] (tgid:5658 range:0x7ae000-0x7af000) could not be found.
CPU: Core Solo / Duo, speed 1000 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) count 100000
samples  %        app name                 symbol name
12731    32.8949  a.out                    levenshtein
11958    30.8976  a.out                    corpora_pass2
5231     13.5161  no-vmlinux               /no-vmlinux
4021     10.3896  a.out                    corpora_pass1
1733      4.4778  libc-2.10.1.so           /lib/tls/i686/cmov/libc-2.10.1.so
542       1.4004  ld-2.10.1.so             /lib/ld-2.10.1.so
398       1.0284  a.out                    method_top_matches

Вот оно: главный потребитель — функция levenshtein(). За этим последовала еще одна команда для создания дизассемблированного вывода, аннотированного исходным кодом и количеством/временем выполнения каждой строки. Это выглядит так (количество/время слева от каждой выполненной строки):

> opannotate --source --assembly ./a.out > report.as.handcoded.1
> cat report.as.handcoded.1

...
...
...
           :         __asm__ (
 2  0.0069 : 804918a:       mov    -0x50(%ebp),%ecx
 4  0.0137 : 804918d:       mov    -0x54(%ebp),%ebx
           : 8049190:       mov    -0x4c(%ebp),%eax
12  0.0412 : 8049193:       cmp    %eax,%ecx
10  0.0344 : 8049195:       cmovbe %ecx,%eax
 8  0.0275 : 8049198:       cmp    %eax,%ebx
11  0.0378 : 804919a:       cmovbe %ebx,%eax
16  0.0550 : 804919d:       mov    %eax,-0x4c(%ebp)
           :                   "cmp     %0, %2\n\t"
           :                   "cmovbe  %2, %0\n\t"
           :                  : "+r"(a) :
           :                    "%r"(b), "r"(c)
           :                  );
           :          return a;
 ...
 ...
 ...
person Sudhanshu    schedule 12.01.2010

Вы можете добиться большего успеха, чем gprof. Рассмотрим снимки стека. Вы можете сделать это с помощью pstack, lsstack (если вы можете его получить) или вручную приостановив работу под отладчиком. Вот краткое введение в технику.

person Mike Dunlavey    schedule 12.01.2010
comment
Майк, разве количество инструкций + затраченное время, выдаваемое программой opannotate, не является более или менее той же техникой (см. мой собственный ответ здесь), которую вы там описали? - person Sudhanshu; 12.01.2010
comment
@Sudhanshu: я только что просмотрел документ oprofile и не уверен, но похоже, что он близок к тому, что, по моему мнению, необходимо. Он производит выборку стека вызовов, но я не могу точно сказать, производит ли она выборку по времени настенных часов. (Некоторые профилировщики не берут пробы во время системных вызовов, и это бесполезно.) Тогда, если число, которое они показывают на месте вызова (или в обычной инструкции), является долей выборок, содержащих этот сайт вызова, то он поступает правильно. Другой профилировщик, который делает это, — RotateRight/Zoom. - person Mike Dunlavey; 12.01.2010
comment
... другое, что ему нужно сделать, это позволить вам контролировать, когда он сэмплирует, а когда нет (например, когда он ожидает ввода пользователя). - person Mike Dunlavey; 12.01.2010
comment
... Я не думаю, что подсчеты полезны. Времена полезны, если они означают долю выборок. Затем я хочу видеть дроби не менее 0,1 или 0,05. По моему опыту, проблемы, которые стоит исправить, как правило, стоят › 10%. - person Mike Dunlavey; 12.01.2010
comment
... Мне нравится видеть фактические образцы стека, а не просто сводные цифры, потому что тогда я могу составить словесное описание того, что именно он делает и почему, и сказать, какую часть времени он это делает. Мне также нравится включать, если необходимо, другую информацию о состоянии (например, что было искомой строкой). Это говорит мне, действительно ли это было необходимо. - person Mike Dunlavey; 12.01.2010
comment
+1 @Mike: Предложенная вами методология хороша, и я думаю, что oprofile более или менее делает такую ​​выборку. Oprofile фактически профилирует всю систему (ядро + все пользовательские программы), хотя я могу указать oprofile выводить отчет для определенного исполняемого файла (в данном случае моей собственной программы). No-vmlinux в опаннотате — это время, проведенное внутри ядра. Однако в моей программе большая часть времени проводится в пользовательском пространстве. И аннотированный дизассемблированный вывод дает мне время, потраченное построчно. Я вижу, к чему вы клоните с примерами стека, и я согласен, что это может дать хорошие идеи. - person Sudhanshu; 12.01.2010
comment
@Sudhanshu: Спасибо, что заставил меня взглянуть на oprofile. Итак, теперь я знаю, что два профилировщика поступают правильно (более или менее). Кстати, если вы берете свои собственные образцы в отладчике, вы можете проследить интерпретируемый стек Ruby (с некоторой работой), если вы хотите изучить возможность оптимизации некоторого кода Ruby. - person Mike Dunlavey; 12.01.2010

Вы можете запустить сам интерпретатор ruby ​​​​через профайлер. Если этого слишком много, напишите небольшую программу на C, которая загружает разделяемую библиотеку и вызывает ее экспортированные функции. Затем профилируйте эту программу на C. Это избавляет вас от необходимости поддерживать две версии библиотеки.

person Vijay Mathew    schedule 12.01.2010
comment
Да, я думаю, я могу профилировать сам интерпретатор ruby ​​- за исключением, возможно, мне придется пройти через много хлама, который меня не интересует. Я думаю, что можно профилировать только сами общие библиотеки, так как я погуглил и нашел ссылки, предлагающие это возможно. За исключением того, что я не получил авторитетной ссылки, в которой говорилось бы, как именно это сделать. - person Sudhanshu; 12.01.2010