View source
<?php
namespace Drupal\xhprof\Controller;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\xhprof\XHProfLib\Report\ReportConstants;
use Drupal\xhprof\XHProfLib\Report\ReportInterface;
use Drupal\xhprof\XHProfLib\Run;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
class XHProfController extends ControllerBase {
private $profiler;
private $reportEngine;
private $dateFormatter;
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
$instance->profiler = $container
->get('xhprof.profiler');
$instance->reportEngine = $container
->get('xhprof.report_engine');
$instance->dateFormatter = $container
->get('date.formatter');
return $instance;
}
public function runsAction() {
$runs = $run = $this->profiler
->getStorage()
->getRuns();
$rows = [];
foreach ($runs as $run) {
$rows[] = [
Link::createFromRoute($run['run_id'], 'xhprof.run', [
'run' => $run['run_id'],
]),
format_size($run['size']),
isset($run['path']) ? $run['path'] : '',
$this->dateFormatter
->format($run['date'], 'small'),
];
}
return [
'table' => [
'#type' => 'table',
'#header' => [
[
'data' => $this
->t('View'),
],
[
'data' => $this
->t('File size'),
],
[
'data' => $this
->t('Path'),
'field' => 'path',
],
[
'data' => $this
->t('Date'),
'field' => 'date',
'sort' => 'desc',
],
],
'#rows' => $rows,
'#empty' => $this
->t('No runs collected'),
'#attributes' => [
'id' => 'xhprof-runs-table',
],
],
];
}
public function runAction(Run $run, Request $request) {
$length = $request->query
->get('length', 100);
$sort = $request->query
->get('sort', 'wt');
$report = $this->reportEngine
->getReport(NULL, NULL, $run, NULL, NULL, $sort, NULL, NULL);
$run_id = $run
->getId();
$build['#title'] = $this
->t('XHProf view report for %id', [
'%id' => $run_id,
]);
$descriptions = ReportConstants::getDescriptions();
$build['summary'] = [
'table' => [
'#type' => 'table',
'#responsive' => FALSE,
'#header' => [
$this
->t('Overall summary'),
$this
->t('%mu is <a href=":wiki">microseconds</a>', [
'%mu' => 'μs',
':wiki' => 'https://en.wikipedia.org/wiki/Microsecond',
]),
],
'#rows' => $this
->getSummaryRows($report, $descriptions),
],
];
$build['length'] = [
'#type' => 'inline_template',
'#template' => $length == -1 ? '<h3>Displaying all functions, sorted by {{ sort }}. [{{ top }}]</h3>' : '<h3>Displaying top {{ length }} functions, sorted by {{ sort }}. [{{ all }}]</h3>',
'#context' => [
'length' => $length,
'all' => Link::createFromRoute($this
->t('show all'), 'xhprof.run', [
'run' => $run_id,
'length' => -1,
]),
'top' => Link::createFromRoute($this
->t('show top'), 'xhprof.run', [
'run' => $run_id,
'length' => 100,
]),
'sort' => Xss::filter($descriptions[$sort], []),
],
];
$build['table'] = [
'#type' => 'table',
'#responsive' => FALSE,
'#sticky' => TRUE,
'#header' => $this
->getRunHeader($report, $descriptions, $run_id),
'#rows' => $this
->getRunRows($run, $report, $length),
'#attributes' => [
'class' => [
'responsive',
],
],
'#attached' => [
'library' => [
'xhprof/xhprof',
],
],
'#cache' => [
'contexts' => [
'url.query_args',
],
],
];
return $build;
}
public function diffAction(Run $run1, Run $run2) {
return [
'#markup' => 'Not working yet',
];
}
public function symbolAction(Run $run, $symbol, Request $request) {
$sort = $request->query
->get('sort', 'wt');
$globalReport = $this->reportEngine
->getReport(NULL, NULL, $run, NULL, NULL, $sort, NULL, NULL);
$report = $this->reportEngine
->getReport(NULL, NULL, $run, NULL, [
$symbol,
], $sort, NULL, NULL);
$build['#title'] = $this
->t('XHProf view report for %id', [
'%id' => $run
->getId(),
]);
$descriptions = ReportConstants::getDescriptions();
$build['title'] = [
'#type' => 'inline_template',
'#template' => '<strong>Parent/Child report for ' . $symbol . '</strong>',
];
$build['table'] = [
'#theme' => 'table',
'#header' => $this
->getRunHeader($report, $descriptions, $run
->getId()),
'#rows' => $this
->getRunRows($run, $report, -1, $globalReport, $symbol),
'#attributes' => [
'class' => [
'responsive',
],
],
'#attached' => [
'library' => [
'xhprof/xhprof',
],
],
];
return $build;
}
private 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,
]);
}
private function getRunHeader(ReportInterface $report, $descriptions, $run_id) {
$headers = [
'fn',
'ct',
'ct_perc',
];
$metrics = $report
->getMetrics();
foreach ($metrics as $metric) {
$headers[] = $metric;
$headers[] = $metric . '_perc';
$headers[] = 'excl_' . $metric;
$headers[] = 'excl_' . $metric . '_perc';
}
$sortable = ReportConstants::getSortableColumns();
foreach ($headers as &$header) {
if (isset($sortable[$header])) {
$header = [
'data' => Link::createFromRoute($descriptions[$header], 'xhprof.run', [
'run' => $run_id,
], [
'query' => [
'sort' => $header,
],
])
->toRenderable(),
];
}
else {
$header = new FormattableMarkup($descriptions[$header], []);
}
}
return $headers;
}
private function getRunRows(Run $run, ReportInterface $report, $length, ReportInterface $globalReport = NULL, $symbol = NULL) {
$rows = [];
$runId = $run
->getId();
$symbols = $report
->getSymbols($length);
if ($symbol) {
$globalSymbols = $globalReport
->getSymbols(-1);
$this
->getCurrentFunctionRows($globalSymbols[$symbol], $rows);
$runSymbols = $run
->getSymbols();
$parents = [];
$children = [];
foreach ($runSymbols as $value) {
if ($value
->getChild() == $symbol && ($parent = $value
->getParent())) {
$parents[$parent] = $globalSymbols[$parent];
}
elseif ($value
->getParent() == $symbol && ($child = $value
->getChild())) {
$children[$child] = $value;
}
}
$this
->getParentFunctionsRows($parents, $runId, $rows);
if (\count($children)) {
$columns = \current($symbols);
$rows[] = [
[
'data' => new FormattableMarkup('<strong>@value</strong>', [
'@value' => $this
->formatPlural(\count($children), 'Child function', 'Child functions (@count)'),
]),
'colspan' => \count($columns),
],
];
}
}
foreach ($symbols as $value) {
if (!$symbol || !empty($children[$value[0]])) {
$text = $value[0];
$url = Url::fromRoute('xhprof.symbol', [
'run' => $runId,
'symbol' => $value[0],
]);
$value[0] = Link::fromTextAndUrl($text, $url)
->toString();
$rows[] = $value;
}
}
return $rows;
}
private function getCurrentFunctionRows($symbol, &$rows) {
$rows[] = [
[
'data' => new FormattableMarkup('<strong>@value</strong>', [
'@value' => $this
->t('Current function'),
]),
'colspan' => \count($symbol),
],
];
$symbol[0] = Link::fromTextAndUrl($symbol[0], Url::fromRoute('<current>'));
$rows[] = $symbol;
return $rows;
}
private function getParentFunctionsRows($parents, $runId, &$rows) {
if (!empty($parents)) {
$columns = \current($parents);
$rows[] = [
[
'data' => new FormattableMarkup('<strong>@value</strong>', [
'@value' => $this
->formatPlural(\count($parents), 'Parent function', 'Parent functions (@count)'),
]),
'colspan' => \count($columns),
],
];
foreach ($parents as $parent) {
$parent[0] = Link::fromTextAndUrl($parent[0], Url::fromRoute('xhprof.symbol', [
'run' => $runId,
'symbol' => $parent[0],
]));
$rows[] = $parent;
}
}
return $rows;
}
private 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;
}
}