Профилирование компонентов 1C-Bitrix

Oct 29, 2010 15:00

Для сбора детальной статистики о производительности (профилирования) отдельных компонентов сайта предлагается использовать следующий набор скриптов. Хотя эти скрипты создавались для одного конкретного сайта, они могут применяться и для других проектов на Битриксе.

profiler.php

Скрипт помещается в DOCUMENT_ROOT сайта. Будет подключаться профилируемыми компонентами для сбора статистики. Статистика сохраняется в файле profiler.log в каталоге DOCUMENT_ROOT. В статистике отмечается время выполнения скриптов отдельных компонентов. Для функций выборки, подлежащих детальному профилированию, сохраняется размер возвращаемых наборов данных (см. ниже @PROFILE_CALLS).



FieldsCount(), $rset->SelectedRowsCount() ));
}

attach-profiler.pl

Применяется для подключения профилировщика к указанным компонентам. Скрипт помещается в каталог на уровень выше, чем DOCUMENT_ROOT сайта. В переменную $BITRIX_ROOT заносится путь к каталогу с библиотеками Битрикса. В переменную @PROFILE_CALLS заносится список вызовов функций для детального профилирования. Вызывается из командной строки, в качестве аргументов через пробел указывается список компонентов в нотации Битрикса. При указании компонентов можно использовать звёздочку для обозначения сразу нескольких компонентов. Примеры использования:

# Подключить профилировшик к компоненту catalog из пространства yes
./attach-profiler.pl yes:catalog

# Подключить к компонентам catalog, catalog.section, catalog.filter из пространства yes
./attach-profiler.pl yes:catalog yes:catalog.section yes:catalog.filter

# Подключить к компоненту catalog и прочим компонентам, начинающимся на catalog, из пространства yes
./attach-profiler.pl yes:catalog yes:catalog.*

# Подключить ко всем компонентам из пространства имён yes
./attach-profiler.pl yes:*

#!/usr/bin/perl

$BITRIX_ROOT = 'htdocs/bitrix';
@PROFILE_CALLS = qw/::GetList $DB->Query ::GetPropertyEnum GetIBlockSectionPath/;

# -----------------------------------------

# Process command-line arguments
@component_list = ();
for $component (@ARGV) {
if ($component =~ m/\*/) {
# Process wildcards
push @component_list, map {
s~^$BITRIX_ROOT/components/~~;
join( ':', split '/', $_);
} glob $BITRIX_ROOT.'/components/'.join( '/', split /:/, $component);
} else {
push @component_list, $component;
}
}

$re_calls = join '|', map quotemeta, @PROFILE_CALLS;

COMPONENT:
for $component (@component_list)
{
# Locate and read source file
$filename = $BITRIX_ROOT.'/components/'.join( '/', split /:/, $component).'/component.php';
open $IN, $filename or die "$component: Cannot open $filename";
@src = ();
push @src, $_ while (<$IN>);
close $IN;

$header_included = 0;
for ($i = 0; $i <= $#src; $i++)
{
$_ = $src[$i];

# Include profiler header
if (!$header_included && m/B_PROLOG_INCLUDED.*die\(.*\);/) {
$header_included = 1;
$comp_id = '$prof_'.int( rand( 1000000));
splice @src, $i+1, 0,
"require_once(\$_SERVER['DOCUMENT_ROOT'].'/profiler.php');\n",
"profilerStart( $comp_id = __FILE__.':'.__LINE__);\n";
$i += 2;
next;
}
if (m/require_once.+profiler\.php/) {
warn "$component already profiled";
next COMPONENT;
}

# Profile selection calls
if (m/^(\s*)(\$\w+)\s*=.*($re_calls)\(/) {
$in_multiline = 1;
($indent, $var) = ($1, $2);
# Search for call terminator
$_ = $src[++$i] until (m/\);/);
splice @src, ++$i, 0,
"${indent}profilerLogResult( __FILE__.':'.__LINE__, $var);\n";
next;
}

# Include profiler footer
if (m/^(\s*)(return.*;|\?>)/) {
die "$component: Profiler header was not included" if ! $header_included;
splice @src, $i++, 0, "$1profilerStop( $comp_id);\n";
next;
}
}

for ($i = 0; $i <= $#src; $i++)
{
$_ = $src[$i];

# Profile top-level cycles
if (m/^(\s*)(while|for|foreach)\s*\(/) {
($indent, $keyword) = ($1, $2);
# Search for instruction start
$i_start = $i;
$i_start-- if $src[$i-1] =~ m/^\s*\w+[^;]*$/;

# Search for instruction end
$i_end = $i;
$_ = $src[++$i_end] until (m/(;|\{|\):\s*$)/);
if ($1 eq '{') {
# Search for matching bracket
$depth = 0;
TERM1:
while ($i_end <= $#src) {
for $tok ($src[$i_end] =~ m/(^|\s)(\{|\})(\s|$)/g) {
$depth += ($2 eq '{') ? 1 : -1;
last TERM1 if ($depth == 0);
}
$i_end++;
}
} elsif ($1 =~ m/\):/) {
# Search for matching keyword
$end_keyword = 'end'.$keyword;
$depth = 0;
TERM2:
while ($i_end <= $#src) {
for $tok ($src[$i_end] =~ m/($end_keyword;|$keyword\s*\(.*\):)/g) {
$depth += ($1 =~ m/$end_keyword/) ? -1 : 1;
last TERM2 if ($depth == 0);
}
$i_end++;
}
}

$cycle_id = '$prof_'.int( rand( 1000000));
splice @src, $i_end+1, 0, "${indent}profilerStop( $cycle_id);\n";
splice @src, $i_start, 0, "${indent}profilerStart( $cycle_id = __FILE__.':'.__LINE__);\n";
$i = $i_end + 2; # $i += 2;
next;
}
}

# Store modified file
$prof_filename = $filename.'.prof';
open $OUT, '>', $prof_filename or die "$component: Cannot create $prof_filename";
print $OUT $_ for (@src);
close $OUT;

# Replace file with modified version
unlink $filename or die "$component: Cannot delete $filename";
rename $prof_filename, $filename or die "$component: Cannot rename file";
}

detach-profiler.pl

Применяется для отключения профилировщика от указанных компонентов, то есть выполняет действия, обратные скрипту attach-profiler.pl. Размещение, вызов и параметры командной строки аналогичны attach-profiler.pl. Примеры использования:

# Отключить профилировщик от всех компонентов из пространства имён yes
./detach-profiler.pl yes:*

#!/usr/bin/perl

$BITRIX_ROOT = 'htdocs/bitrix';

# -----------------------------------------

# Process command-line arguments
@component_list = ();
for $component (@ARGV) {
if ($component =~ m/\*/) {
# Process wildcards
push @component_list, map {
s~^$BITRIX_ROOT/components/~~;
join( ':', split '/', $_);
} glob $BITRIX_ROOT.'/components/'.join( '/', split /:/, $component);
} else {
push @component_list, $component;
}
}

for $component (@component_list) {
# Locate and open files
$filename = $BITRIX_ROOT.'/components/'.join( '/', split /:/, $component).'/component.php';
$rest_filename = $filename.'.rest';
open $IN, $filename or die "$component: Cannot open $filename";
open $OUT, '>', $rest_filename or die "$component: Cannot create $rest_filename";

while (<$IN>)
{
# Skip profiler header
next if m/require_once.+profiler\.php/;

# Skip profiler calls
next if m/^\s*profiler\w+\(.*\);/;

# Pass through
print $OUT $_;
}
close $IN;
close $OUT;

# Replace file with modified version
unlink $filename or die "$component: Cannot delete $filename";
rename $rest_filename, $filename or die "$component: Cannot rename file";
}

Previous post Next post
Up