diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index a954832b529b..e53a7cfdf14d 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -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 .= ''; - $output .= "{$row['name']}"; - $output .= "{$row['component']}"; - $output .= "" . number_format($row['duration'] * 1000, 2) . ' ms'; + $hasChildren = isset($row['children']) && !empty($row['children']); + $open = $row['name'] === 'Controller'; + + if ($hasChildren) { + $output .= ''; + } else { + $output .= ''; + } + $output .= '' . ($hasChildren ? '' : '') . $row['name'] . ''; + $output .= '' . $row['component'] . ''; + $output .= '' . number_format($row['duration'] * 1000, 2) . ' ms'; $output .= ""; $offset = ((((float) $row['start'] - $startTime) * 1000) / $displayTime) * 100; @@ -217,6 +247,19 @@ protected function renderTimeline(array $collectors, float $startTime, int $segm $output .= ''; $styleCount++; + + // Add children if any + if ($hasChildren) { + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= $this->renderTimelineRecursive($row['children'], $startTime, $segmentCount, $segmentDuration, $styles, $styleCount, $level + 1, true); + $output .= ''; + $output .= '
'; + $output .= ''; + $output .= ''; + } } return $output; @@ -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)); + } + //-------------------------------------------------------------------- /** diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index f881dd18d6ec..bc8b4ac44ef7 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -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; @@ -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; diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index ca38f9b47bb5..fd7292c96765 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -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'); + } + }, + + //-------------------------------------------------------------------- /**