qtools_profiler-8.x-1.x-dev/src/Plugin/Profiler/TidewaysXhprof.php
src/Plugin/Profiler/TidewaysXhprof.php
<?php
namespace Drupal\qtools_profiler\Plugin\Profiler;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Url;
use Drupal\qtools_profiler\ProfilerPluginInterface;
use Drupal\qtools_profiler\XHProfLib\Parser\Parser;
use Drupal\qtools_profiler\XHProfLib\Report\Report;
use Drupal\qtools_profiler\XHProfLib\Report\ReportConstants;
use Drupal\qtools_profiler\XHProfLib\Report\ReportInterface;
use Drupal\qtools_profiler\XHProfLib\Run;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides TidewaysXhprof profiler plugin.
*
* @Plugin(
* id = "tideways_xhprof",
* label = @Translation("Tideways xhprof"),
* description = @Translation("Tideways xhprof php profiler.")
* )
*/
class TidewaysXhprof extends PluginBase implements ProfilerPluginInterface {
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function isLoaded() {
return extension_loaded('tideways_xhprof');
}
/**
* {@inheritdoc}
*/
public function getSettingsForm(array $conf) {
$profiler_options = [
'FLAGS_CPU' => t('FLAGS_CPU'),
'FLAGS_MEMORY' => t('FLAGS_MEMORY'),
];
$form['options'] = [
'#type' => 'checkboxes',
'#title' => t('Profiling options'),
'#default_value' => $conf['profiling']['profilers']['tideways_xhprof']['options'] ?? [],
'#options' => $profiler_options,
'#description' => t('Support depends on particular extension'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function enable($options) {
if (!static::isLoaded()) {
return;
}
$flags = 0;
if (!empty($options['options']['FLAGS_CPU'])) {
$flags = $flags | TIDEWAYS_XHPROF_FLAGS_CPU;
}
if (!empty($options['options']['FLAGS_MEMORY'])) {
$flags = $flags | TIDEWAYS_XHPROF_FLAGS_MEMORY;
}
tideways_xhprof_enable($flags);
}
/**
* {@inheritdoc}
*/
public function disable() {
if (!static::isLoaded()) {
return [];
}
return tideways_xhprof_disable();
}
/**
* {@inheritdoc}
*/
public function buildProfilePage(Request $request, $id) {
$route_name = $request->attributes->get('_route');
// Build run from data.
$reportService = \Drupal::service('qtools_profiler.report');
$data = $reportService->getProfileDetails($id);
if (empty($data)) {
throw new NotFoundHttpException();
}
// Parse run.
$run = new Run($id, 'space', $data);
// Get filter/sort.
$length = $request->get('length', 100);
$sort = $request->get('sort', 'excl_wt');
$parser = new Parser($run, $sort, NULL);
$report = new Report($parser);
$build['#title'] = t('XHProf view report for %id', ['%id' => $run->getId()]);
$descriptions = ReportConstants::getDescriptions();
$build['summary'] = [
'title' => [
'#type' => 'inline_template',
'#template' => '<h3>Summary</h3>',
],
'table' => [
'#theme' => 'table',
'#header' => [],
'#rows' => $this->getSummaryRows($report, $descriptions),
],
];
$build['length'] = [
'#type' => 'inline_template',
'#template' => ($length == -1) ? '<h3>Displaying all functions, sorted by <em>{{ sort }}</em>.</h3>' : '<h3>Displaying top {{ length }} functions, sorted by {{ sort }}. [{{ all }}]</h3>',
'#context' => [
'length' => $length,
'all' => \Drupal::service('link_generator')->generate(t('show all'), Url::fromRoute($route_name, [
'id' => $run->getId(),
'length' => -1,
])),
'sort' => Xss::filter($descriptions[$sort], []),
],
];
$build['table'] = [
'#theme' => 'table',
'#header' => $this->getRunHeader($report, $descriptions, $run, $route_name),
'#rows' => $this->getRunRows($report, $length),
'#attributes' => ['class' => ['responsive']],
];
return $build;
}
/**
* Get rows summary from report.
*/
protected function getSummaryRows(ReportInterface $report, $descriptions) {
$summaryRows = [];
$possibileMetrics = $report->getPossibleMetrics();
foreach ($report->getSummary() as $metric => $value) {
$key = 'Total ' . Xss::filter($descriptions[$metric], []);
$unit = isset($possibileMetrics[$metric]) ? $possibileMetrics[$metric][1] : '';
$value = new FormattableMarkup('@value @unit', [
'@value' => $value,
'@unit' => $unit,
]);
$summaryRows[] = [
$key,
$value,
];
}
return $summaryRows;
}
/**
* Ger run header from report.
*/
protected function getRunHeader(ReportInterface $report, $descriptions, Run $run, $route_name) {
$headers = ['fn', 'ct', 'ct_perc'];
$metrics = $report->getMetrics();
foreach ($metrics as $metric) {
$headers[] = $metric;
$headers[] = $metric . '_perc';
$headers[] = 'excl_' . $metric;
$headers[] = 'excl_' . $metric . '_perc';
}
foreach ($headers as &$header) {
$description = strip_tags($descriptions[$header]);
$link = \Drupal::service('link_generator')->generate($description, Url::fromRoute($route_name, [
'id' => $run->getId(),
'length' => -1,
'sort' => str_replace('_perc', '', $header),
]));
$header = new FormattableMarkup($link, []);
}
return $headers;
}
/**
* Get run Rows from report.
*/
protected function getRunRows(ReportInterface $report, $length) {
$symbols = $report->getSymbols($length);
foreach ($symbols as &$symbol) {
$symbol[0] = $this->abbrClass($symbol[0]);
}
return $symbols;
}
/**
* Theme class.
*/
protected function abbrClass($class) {
$parts = explode('\\', $class);
$short = array_pop($parts);
if (strlen($short) >= 40) {
$short = substr($short, 0, 30) . " … " . substr($short, -5);
}
return new FormattableMarkup('<abbr title="@class">@short</abbr>', [
'@class' => $class,
'@short' => $short,
]);
}
}
