View source
<?php
define('XHPROF_PATH', 'admin/reports/xhprof');
function xhprof_menu() {
$items = array();
$items[XHPROF_PATH] = array(
'title' => 'XHProf runs',
'page callback' => 'xhprof_run_list',
'access arguments' => array(
'access xhprof data',
),
'description' => 'View XHProf profiling data.',
);
$items[XHPROF_PATH . '/%'] = array(
'title' => 'XHProf view',
'page callback' => 'xhprof_display_page',
'page arguments' => array(
3,
),
'access arguments' => array(
'access xhprof data',
),
);
$items[XHPROF_PATH . '/diff/%/%'] = array(
'title' => 'XHProf view',
'page callback' => 'xhprof_display_diff_page',
'page arguments' => array(
4,
5,
),
'access arguments' => array(
'access xhprof data',
),
);
$items[XHPROF_PATH . '/%/symbol/%'] = array(
'title' => 'XHProf view',
'page callback' => 'xhprof_display_page',
'page arguments' => array(
3,
5,
),
'access arguments' => array(
'access xhprof data',
),
);
$items['admin/config/development/xhprof'] = array(
'title' => 'XHProf settings',
'description' => 'Configure XHProf profiler settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'xhprof_admin_settings',
),
'file' => 'xhprof.admin.inc',
'access arguments' => array(
'administer site configuration',
),
);
return $items;
}
function xhprof_permission() {
return array(
'access xhprof data' => array(
'title' => t('Access XHProf data'),
),
);
}
function xhprof_theme() {
return array(
'xhprof_overall_summary' => array(
'variables' => array(
'totals' => NULL,
'possible_metrics' => NULL,
'metrics' => NULL,
'display_calls' => NULL,
),
),
'xhprof_run_table' => array(
'variables' => array(
'stats' => NULL,
'totals' => NULL,
'url_params' => NULL,
'title' => NULL,
'flat_data' => NULL,
'limit' => NULL,
),
),
);
}
function xhprof_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'xhprof'),
);
}
function xhprof_require() {
require_once dirname(__FILE__) . '/xhprof.inc';
}
function xhprof_xhprof_enable() {
if (xhprof_is_enabled()) {
xhprof_require();
$flags = 0;
if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
$flags = $flags + XHPROF_FLAGS_NO_BUILTINS;
}
if (variable_get('xhprof_flags_cpu', TRUE)) {
$flags = $flags + XHPROF_FLAGS_CPU;
}
if (variable_get('xhprof_flags_memory', TRUE)) {
$flags = $flags + XHPROF_FLAGS_MEMORY;
}
xhprof_enable($flags);
return TRUE;
}
else {
return FALSE;
}
}
function xhprof_is_enabled() {
$enabled = FALSE;
if (xhprof_extension_check() && variable_get('xhprof_enabled', FALSE)) {
$enabled = TRUE;
if (arg(0) == 'admin' && variable_get('xhprof_disable_admin_paths', TRUE)) {
$enabled = FALSE;
}
if (arg(0) == 'js' && arg(1) == 'admin_menu' && variable_get('xhprof_disable_admin_menu_paths', TRUE)) {
$enabled = FALSE;
}
$interval = variable_get('xhprof_interval', 1);
if ($interval && mt_rand(1, $interval) % $interval != 0) {
$enabled = FALSE;
}
}
return $enabled;
}
function xhprof_boot() {
if (xhprof_xhprof_enable()) {
drupal_register_shutdown_function('xhprof_shutdown');
}
}
function xhprof_shutdown() {
global $xhprof_run_id;
if (!module_exists('xhprof')) {
return;
}
if (xhprof_is_enabled() && ($xhprof_run_id = xhprof_shutdown_xhprof())) {
drupal_register_shutdown_function('xhprof_shutdown_real');
if (function_exists('drush_log')) {
drush_log('xhprof link: ' . xhprof_link($xhprof_run_id, 'url'), 'notice');
}
}
}
function xhprof_shutdown_real() {
global $user;
if (function_exists('drupal_get_http_header')) {
$header = drupal_get_http_header('content-type');
if ($header) {
$formats = array(
'xml',
'javascript',
'json',
'plain',
'image',
'application',
'csv',
'x-comma-separated-values',
);
foreach ($formats as $format) {
if (strstr($header, $format)) {
return;
}
}
}
}
$output = $txt = '';
if (isset($user) && user_access('access xhprof data')) {
$output .= '<div class="xhprof-ui">' . xhprof_link($GLOBALS['xhprof_run_id']) . '</div>';
}
if ($output) {
print $output;
}
}
function xhprof_shutdown_xhprof() {
$namespace = variable_get('site_name', '');
$xhprof_data = xhprof_disable();
$class = variable_get('xhprof_default_class', 'XHProfRunsFile');
$xhprof_runs = new $class();
return $xhprof_runs
->save_run($xhprof_data, $namespace);
}
function xhprof_link($run_id, $type = 'link') {
$url = url(XHPROF_PATH . '/' . $run_id, array(
'absolute' => TRUE,
));
return $type == 'url' ? $url : l(t('XHProf output'), $url);
}
function xhprof_include() {
static $included = FALSE;
if (!$included) {
module_load_include('inc', 'xhprof');
module_invoke_all('xhprof_load_classes');
module_load_include('inc', 'xhprof', 'XHProfRunsInterface');
module_load_include('inc', 'xhprof', 'XHProfRunsFile');
$included = TRUE;
}
}
function _xhprof_get_object() {
static $xhprof_object = NULL;
if (empty($xhprof_object)) {
$class = variable_get('xhprof_default_class', 'XHProfRunsFile');
if (class_exists($class)) {
$xhprof_object = new $class();
}
else {
watchdog('xhprof', 'Unable to load default class %class!', array(
'%class' => $class,
), WATCHDOG_CRITICAL);
}
}
return $xhprof_object;
}
function xhprof_get_classes() {
xhprof_include();
$classes = array(
'XHProfRunsFile',
);
drupal_alter('xhprof_classes', $classes);
return $classes;
}
function xhprof_run_list() {
global $pager_page_array, $pager_total, $pager_total_items;
xhprof_include();
$page = isset($_GET['page']) ? $_GET['page'] : '';
$element = 0;
$limit = 50;
$class = variable_get('xhprof_default_class', 'XHProfRunsFile');
$xhprof_runs_impl = new $class();
$pager_page_array = array(
$page,
);
$pager_total_items[$element] = $xhprof_runs_impl
->getCount();
$pager_total[$element] = ceil($pager_total_items[$element] / $limit);
$pager_start = $page * 50;
$pager_end = $pager_start + 50;
$runs = $xhprof_runs_impl
->getRuns(array(), $limit);
$header = array();
$header[] = array(
'data' => t('View'),
);
$header[] = array(
'data' => t('Path'),
'field' => 'path',
);
$header[] = array(
'data' => t('Date'),
'field' => 'date',
'sort' => 'desc',
);
$rows = array();
foreach ($runs as $run) {
$row = array();
$link = XHPROF_PATH . '/' . $run['run_id'];
$row[] = array(
'data' => l($run['run_id'], $link),
);
$row[] = array(
'data' => isset($run['path']) ? $run['path'] : '',
);
$row[] = array(
'data' => format_date($run['date'], 'small'),
);
$rows[] = $row;
}
$attributes = array(
'id' => 'xhprof-runs-table',
);
$output = theme('table', array(
'header' => $header,
'rows' => $rows,
'attributes' => $attributes,
));
$output .= theme('pager');
return $output;
}
function xhprof_param_defaults() {
return array(
'run' => '',
'wts' => '',
'symbol' => '',
'sort' => 'wt',
'run1' => '',
'run2' => '',
'source' => 'xhprof',
'all' => 0,
);
}
function xhprof_display_page($run_id, $symbol = NULL) {
drupal_add_css(drupal_get_path('module', 'xhprof') . '/xhprof.css');
return xhprof_display_run(array(
$run_id,
), $symbol);
}
function xhprof_display_diff_page($run1, $run2, $symbol = NULL) {
drupal_add_css(drupal_get_path('module', 'xhprof') . '/xhprof.css');
return xhprof_display_run(array(
$run1,
$run2,
), $symbol = NULL);
}
function xhprof_display_run($run_ids, $symbol = NULL) {
xhprof_require();
if ($symbol !== NULL) {
$symbol = urldecode($symbol);
}
if (count($run_ids) === 1) {
$_GET['run'] = $run_ids[0];
$run_id = $run_ids[0];
}
else {
$_GET['run1'] = $run_ids[0];
$run1 = $run_ids[0];
$_GET['run2'] = $run_ids[1];
$run2 = $run_ids[1];
}
$source = variable_get('site_name', '');
$_GET['source'] = $source;
$url_params = xhprof_param_defaults();
$required_params = array(
'sort',
);
foreach ($url_params as $param => &$value) {
if (isset($_GET[$param])) {
$value = $_GET[$param];
}
elseif (!in_array($param, $required_params)) {
unset($url_params[$param]);
}
}
extract($url_params);
$class = variable_get('xhprof_default_class', 'XHProfRunsFile');
$xhprof_runs_impl = new $class();
$output = '';
if (isset($run_id)) {
$runs_array = explode(",", $run_id);
if (isset($_GET['order'])) {
$sort = xhprof_stat_description($_GET['order'], TRUE);
}
if (count($runs_array) == 1) {
$xhprof_data = $xhprof_runs_impl
->get_run($runs_array[0], $source, $description, $sort);
}
else {
if (!empty($wts)) {
$wts_array = explode(",", $wts);
}
else {
$wts_array = NULL;
}
$data = xhprof_aggregate_runs($xhprof_runs_impl, $runs_array, $wts_array, $source, FALSE);
$xhprof_data = $data['raw'];
$description = $data['description'];
}
xhprof_init_metrics($xhprof_data, $symbol, $sort, FALSE);
$output .= xhprof_profiler_report($url_params, $symbol, $sort, $run_id, $description, $xhprof_data);
}
elseif ($run1 && $run2) {
$xhprof_data1 = $xhprof_runs_impl
->get_run($run1, $source, $description1);
$xhprof_data2 = $xhprof_runs_impl
->get_run($run2, $source, $description2);
$output .= xhprof_init_metrics($xhprof_data2, $symbol, $sort, TRUE);
$output .= xhprof_profiler_report($url_params, $symbol, $sort, $run1, $description1, $xhprof_data1, $run2, $description2, $xhprof_data2);
}
else {
$output .= "No XHProf runs specified in the URL.";
}
return $output;
}
function xhprof_scandir($dir, $source) {
if (is_dir($dir)) {
$runs = array();
foreach (glob("{$dir}/*.{$source}") as $file) {
list($run, $source) = explode('.', basename($file));
$runs[] = array(
'run_id' => $run,
'source' => $source,
'basename' => htmlentities(basename($file)),
'date' => date("Y-m-d H:i:s", filemtime($file)),
);
}
}
return array_reverse($runs);
}
function theme_xhprof_overall_summary($variables) {
$output = '';
extract($variables);
$rows = array();
foreach ((array) $metrics as $metric) {
$rows[] = array(
'<strong>Total ' . xhprof_stat_description($metric) . ':</strong>',
number_format($totals[$metric]) . " " . $possible_metrics[$metric][1],
);
}
if ($display_calls) {
$rows[] = array(
"<strong>Number of function xhprof_Calls:</strong>",
number_format($totals['ct']),
);
}
$header = array(
array(
'data' => 'Overall Summary',
'colspan' => 2,
),
);
$attributes = array(
'class' => array(
'xhprof-table',
'xhprof-summary-table',
),
);
$output .= theme('table', array(
'header' => $header,
'rows' => $rows,
'attributes' => $attributes,
));
return $output;
}
function theme_xhprof_run_table($variables) {
extract($variables);
global $base_path;
$output = '';
$header = array();
foreach ($stats as $stat) {
$desc = xhprof_stat_description($stat);
if (array_key_exists($stat, xhprof_sortable_columns($stat))) {
if (isset($_GET['sort']) && $stat == $_GET['sort']) {
$header_desc = l(t($desc), current_path(), array(
'query' => array(
'sort' => $stat,
),
t($desc),
));
$header[] = array(
'data' => t($header_desc) . theme('tablesort_indicator', array(
'style' => 'desc',
)),
);
}
else {
$header_desc = l(t($desc), current_path(), array(
'query' => array(
'sort' => $stat,
),
t($desc),
));
$header[] = array(
'data' => t($header_desc),
);
}
}
else {
$header[] = array(
'data' => t($desc),
);
}
}
$rows = array();
$i = 0;
foreach ($flat_data as $data) {
$row = array(
array(
'data' => l($data["fn"], xhprof_path_for_symbol($data["fn"], $run1)),
'class' => 'xhprof_symbol',
),
xhprof_print_num_cell($data['ct'], NULL, TRUE),
xhprof_print_pct_cell($data['ct'], $totals['ct'], TRUE),
xhprof_print_num_cell($data['wt'], NULL, TRUE),
xhprof_print_pct_cell($data['wt'], $totals['wt'], TRUE),
xhprof_print_num_cell($data['excl_wt'], NULL, TRUE),
xhprof_print_pct_cell($data['excl_wt'], $totals['wt'], TRUE),
);
if (isset($data['cpu'])) {
$row = array_merge($row, array(
xhprof_print_num_cell($data['cpu'], NULL, TRUE),
xhprof_print_pct_cell($data['cpu'], $totals['cpu'], TRUE),
xhprof_print_num_cell($data['excl_cpu'], NULL, TRUE),
xhprof_print_pct_cell($data['excl_cpu'], $totals['cpu'], TRUE),
));
}
if (isset($data['mu'])) {
$row = array_merge($row, array(
xhprof_print_num_cell($data['mu'], NULL, TRUE),
xhprof_print_pct_cell($data['mu'], $totals['mu'], TRUE),
xhprof_print_num_cell($data['excl_mu'], NULL, TRUE),
xhprof_print_pct_cell($data['excl_mu'], $totals['mu'], TRUE),
xhprof_print_num_cell($data['pmu'], NULL, TRUE),
xhprof_print_pct_cell($data['pmu'], $totals['pmu'], TRUE),
xhprof_print_num_cell($data['excl_pmu'], NULL, TRUE),
xhprof_print_pct_cell($data['excl_pmu'], $totals['pmu'], TRUE),
));
}
$rows[] = $row;
$i++;
if ($limit && $i >= $limit) {
break;
}
}
$size = count($flat_data);
if (!$limit) {
$limit = $size;
$display_link = "";
}
else {
$display_link = l(" [ <strong class=bubble>display all </strong>]", current_path(), array(
'query' => xhprof_array_set($url_params, 'all', 1),
'html' => TRUE,
));
}
$output .= "<h3 align=center>{$title} {$display_link}</h3><br>";
$attributes = array(
'class' => array(
'xhprof-table',
'xhprof-run-table',
),
);
$output .= theme('table', array(
'header' => $header,
'rows' => $rows,
'attributes' => $attributes,
));
return $output;
}
function xhprof_print_num_cell($num, $fmt_func = NULL, $bold = FALSE, $attributes = NULL) {
return array(
'data' => call_user_func_array('xhprof_print_num', func_get_args()),
'class' => 'xhprof_micro',
'align' => 'right',
);
}
function xhprof_print_pct_cell($numer, $denom, $bold = FALSE, $attributes = NULL) {
return array(
'data' => call_user_func_array('xhprof_print_pct', func_get_args()),
'class' => 'xhprof_percent',
'align' => 'right',
);
}
function xhprof_path_for_run($run1, $run2 = NULL) {
if ($run2 === NULL || $run2 === 0) {
$path = XHPROF_PATH . '/' . $run1;
}
else {
$path = XHPROF_PATH . '/diff/' . $run1 . '/' . $run2;
}
return $path;
}
function xhprof_path_for_symbol($symbol, $run1, $run2 = NULL) {
$run_path = xhprof_path_for_run($run1, $run2);
return $run_path . '/symbol/' . urlencode($symbol);
}
function xhprof_extension_check() {
return extension_loaded('xhprof') || extension_loaded('tideways_xhprof');
}
if (extension_loaded('tideways_xhprof') && !extension_loaded('xhprof')) {
function xhprof_disable() {
return call_user_func_array('tideways_xhprof_disable', func_get_args());
}
function xhprof_enable() {
return call_user_func_array('tideways_xhprof_enable', func_get_args());
}
define('XHPROF_FLAGS_CPU', TIDEWAYS_XHPROF_FLAGS_CPU);
define('XHPROF_FLAGS_MEMORY', TIDEWAYS_XHPROF_FLAGS_MEMORY);
define('XHPROF_FLAGS_NO_BUILTINS', TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS);
}