Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Debug] [4.2] Structure debug toolbar timeline with collapsible elements. #4886

Merged
merged 1 commit into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 92 additions & 10 deletions system/Debug/Toolbar.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,47 @@ public function run(float $startTime, float $totalTime, RequestInterface $reques
* @return string
*/
protected function renderTimeline(array $collectors, float $startTime, int $segmentCount, int $segmentDuration, array &$styles): string
{
$rows = $this->collectTimelineData($collectors);
$output = '';
$styleCount = 0;

// Use recursive render function
return $this->renderTimelineRecursive($rows, $startTime, $segmentCount, $segmentDuration, $styles, $styleCount);
}

/**
* Recursively renders timeline elements and their children.
*
* @param array $rows
* @param float $startTime
* @param int $segmentCount
* @param int $segmentDuration
* @param array $styles
* @param int $styleCount
* @param int $level
* @param bool $isChild
*
* @return string
*/
protected function renderTimelineRecursive(array $rows, float $startTime, int $segmentCount, int $segmentDuration, array &$styles, int &$styleCount, int $level = 0, bool $isChild = false): string
{
$displayTime = $segmentCount * $segmentDuration;
$rows = $this->collectTimelineData($collectors);
$output = '';
$styleCount = 0;

$output = '';

foreach ($rows as $row) {
$output .= '<tr>';
$output .= "<td>{$row['name']}</td>";
$output .= "<td>{$row['component']}</td>";
$output .= "<td class='debug-bar-alignRight'>" . number_format($row['duration'] * 1000, 2) . ' ms</td>';
$hasChildren = isset($row['children']) && !empty($row['children']);
$open = $row['name'] === 'Controller';

if ($hasChildren) {
$output .= '<tr class="timeline-parent' . ($open ? ' timeline-parent-open' : '') . '" id="timeline-' . $styleCount . '_parent" onclick="ciDebugBar.toggleChildRows(\'timeline-' . $styleCount . '\');">';
} else {
$output .= '<tr>';
}
$output .= '<td class="' . ($isChild ? 'debug-bar-width30' : '') . '" style="--level: ' . $level . ';">' . ($hasChildren ? '<nav></nav>' : '') . $row['name'] . '</td>';
$output .= '<td class="' . ($isChild ? 'debug-bar-width10' : '') . '">' . $row['component'] . '</td>';
$output .= '<td class="' . ($isChild ? 'debug-bar-width10 ' : '') . 'debug-bar-alignRight">' . number_format($row['duration'] * 1000, 2) . ' ms</td>';
$output .= "<td class='debug-bar-noverflow' colspan='{$segmentCount}'>";

$offset = ((((float) $row['start'] - $startTime) * 1000) / $displayTime) * 100;
Expand All @@ -217,6 +247,19 @@ protected function renderTimeline(array $collectors, float $startTime, int $segm
$output .= '</tr>';

$styleCount++;

// Add children if any
if ($hasChildren) {
$output .= '<tr class="child-row" id="timeline-' . ($styleCount - 1) . '_children" style="' . ($open ? '' : 'display: none;') . '">';
$output .= '<td colspan="' . ($segmentCount + 3) . '" class="child-container">';
$output .= '<table class="timeline">';
$output .= '<tbody>';
$output .= $this->renderTimelineRecursive($row['children'], $startTime, $segmentCount, $segmentDuration, $styles, $styleCount, $level + 1, true);
$output .= '</tbody>';
$output .= '</table>';
$output .= '</td>';
$output .= '</tr>';
}
}

return $output;
Expand Down Expand Up @@ -246,15 +289,54 @@ protected function collectTimelineData($collectors): array

// Sort it
$sortArray = [
array_column($data, 'start'), SORT_NUMERIC, SORT_ASC,
array_column($data, 'duration'), SORT_NUMERIC, SORT_DESC,
&$data
array_column($data, 'start'), SORT_NUMERIC, SORT_ASC,
array_column($data, 'duration'), SORT_NUMERIC, SORT_DESC,
&$data,
];
array_multisort(...$sortArray);

// Add end time to each element
array_walk($data, static function(&$row) {
$row['end'] = $row['start'] + $row['duration'];
});

// Group it
$data = $this->structureTimelineData($data);

return $data;
}

/**
* Arranges the already sorted timeline data into a parent => child structure.
*
* @param array $elements
*
* @return array
*/
protected function structureTimelineData(array $elements): array
{
// We define ourselves as the first element of the array
$element = array_shift($elements);

// If we have children behind us, collect and attach them to us
while (!empty($elements) && $elements[array_key_first($elements)]['end'] <= $element['end']) {
$element['children'][] = array_shift($elements);
}

// Make sure our children know whether they have children, too
if (isset($element['children'])) {
$element['children'] = $this->structureTimelineData($element['children']);
}

// If we have no younger siblings, we can return
if (empty($elements)) {
return [$element];
}

// Make sure our younger siblings know their relatives, too
return array_merge([$element], $this->structureTimelineData($elements));
}

//--------------------------------------------------------------------

/**
Expand Down
25 changes: 24 additions & 1 deletion system/Debug/Toolbar/Views/toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,22 @@
#debug-bar .timeline {
margin-left: 0;
width: 100%; }
#debug-bar .timeline tr.timeline-parent {
cursor: pointer; }
#debug-bar .timeline tr.timeline-parent td:first-child nav {
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") no-repeat scroll 0 0/15px 75px transparent;
background-position: 0 25%;
display: inline-block;
height: 15px;
width: 15px;
margin-right: 3px;
vertical-align: middle; }
#debug-bar .timeline tr.timeline-parent.timeline-parent-open td:first-child nav {
background-position: 0 75%; }
#debug-bar .timeline tr.timeline-parent.timeline-parent-open {
background-color: #DFDFDF; }
#debug-bar .timeline tr.child-row:hover {
background: transparent; }
#debug-bar .timeline th {
border-left: 1px solid;
font-size: 12px;
Expand All @@ -200,7 +216,14 @@
padding: 5px;
position: relative; }
#debug-bar .timeline td:first-child {
border-left: 0; }
border-left: 0;
max-width: none; }
#debug-bar .timeline td.child-container {
padding: 0px; }
#debug-bar .timeline td.child-container .timeline{
margin: 0px; }
#debug-bar .timeline td.child-container td:first-child:not(.child-container){
padding-left: calc(5px + 10px * var(--level)); }
#debug-bar .timeline .timer {
border-radius: 4px;
-moz-border-radius: 4px;
Expand Down
20 changes: 20 additions & 0 deletions system/Debug/Toolbar/Views/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,26 @@ var ciDebugBar = {
}
},

/**
* Toggle display of timeline child elements
*
* @param obj
*/
toggleChildRows : function (obj) {
if (typeof obj == 'string')
{
par = document.getElementById(obj + '_parent')
obj = document.getElementById(obj + '_children');
}

if (par && obj)
{
obj.style.display = obj.style.display == 'none' ? '' : 'none';
par.classList.toggle('timeline-parent-open');
}
},


//--------------------------------------------------------------------

/**
Expand Down