215 lines
7.8 KiB
Twig
215 lines
7.8 KiB
Twig
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
|
|
|
{% import _self as helper %}
|
|
|
|
{% block toolbar %}
|
|
{% set has_time_events = collector.events|length > 0 %}
|
|
{% set total_time = has_time_events ? '%.0f'|format(collector.duration) : 'n/a' %}
|
|
{% set initialization_time = collector.events|length ? '%.0f'|format(collector.inittime) : 'n/a' %}
|
|
{% set status_color = has_time_events and collector.duration > 1000 ? 'yellow' %}
|
|
|
|
{% set icon %}
|
|
{{ include('@WebProfiler/Icon/time.svg') }}
|
|
<span class="sf-toolbar-value">{{ total_time }}</span>
|
|
<span class="sf-toolbar-label">ms</span>
|
|
{% endset %}
|
|
|
|
{% set text %}
|
|
<div class="sf-toolbar-info-piece">
|
|
<b>Total time</b>
|
|
<span>{{ total_time }} ms</span>
|
|
</div>
|
|
<div class="sf-toolbar-info-piece">
|
|
<b>Initialization time</b>
|
|
<span>{{ initialization_time }} ms</span>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
|
{% endblock %}
|
|
|
|
{% block menu %}
|
|
<span class="label">
|
|
<span class="icon">{{ include('@WebProfiler/Icon/time.svg') }}</span>
|
|
<strong>Performance</strong>
|
|
</span>
|
|
{% endblock %}
|
|
|
|
{% block panel %}
|
|
{% set has_time_events = collector.events|length > 0 %}
|
|
<h2>Performance metrics</h2>
|
|
|
|
<div class="metrics">
|
|
<div class="metric">
|
|
<span class="value">{{ '%.0f'|format(collector.duration) }} <span class="unit">ms</span></span>
|
|
<span class="label">Total execution time</span>
|
|
</div>
|
|
|
|
<div class="metric">
|
|
<span class="value">{{ '%.0f'|format(collector.inittime) }} <span class="unit">ms</span></span>
|
|
<span class="label">Symfony initialization</span>
|
|
</div>
|
|
|
|
{% if profile.collectors.memory %}
|
|
<div class="metric">
|
|
<span class="value">{{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} <span class="unit">MiB</span></span>
|
|
<span class="label">Peak memory usage</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if profile.children|length > 0 %}
|
|
<div class="metric-divider"></div>
|
|
|
|
<div class="metric">
|
|
<span class="value">{{ profile.children|length }}</span>
|
|
<span class="label">Sub-Request{{ profile.children|length > 1 ? 's' }}</span>
|
|
</div>
|
|
|
|
{% if has_time_events %}
|
|
{% set subrequests_time = 0 %}
|
|
{% for child in profile.children %}
|
|
{% set subrequests_time = subrequests_time + child.getcollector('time').events.__section__.duration %}
|
|
{% endfor %}
|
|
{% else %}
|
|
{% set subrequests_time = 'n/a' %}
|
|
{% endif %}
|
|
|
|
<div class="metric">
|
|
<span class="value">{{ subrequests_time }} <span class="unit">ms</span></span>
|
|
<span class="label">Sub-Request{{ profile.children|length > 1 ? 's' }} time</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<h2>Execution timeline</h2>
|
|
|
|
{% if not collector.isStopwatchInstalled() %}
|
|
<div class="empty">
|
|
<p>The Stopwatch component is not installed. If you want to see timing events, run: <code>composer require symfony/stopwatch</code>.</p>
|
|
</div>
|
|
{% elseif collector.events is empty %}
|
|
<div class="empty">
|
|
<p>No timing events have been recorded. Check that symfony/stopwatch is installed and debugging enabled in the kernel.</p>
|
|
</div>
|
|
{% else %}
|
|
{{ block('panelContent') }}
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block panelContent %}
|
|
<form id="timeline-control" action="" method="get">
|
|
<input type="hidden" name="panel" value="time">
|
|
<label for="threshold">Threshold</label>
|
|
<input type="number" name="threshold" id="threshold" value="1" min="0" placeholder="1.1"> ms
|
|
<span class="help">(timeline only displays events with a duration longer than this threshold)</span>
|
|
</form>
|
|
|
|
{% if profile.parent %}
|
|
<h3 class="dump-inline">
|
|
Sub-Request {{ profiler_dump(profile.getcollector('request').requestattributes.get('_controller')) }}
|
|
<small>
|
|
{{ collector.events.__section__.duration }} ms
|
|
<a class="newline" href="{{ path('_profiler', { token: profile.parent.token, panel: 'time' }) }}">Return to parent request</a>
|
|
</small>
|
|
</h3>
|
|
{% elseif profile.children|length > 0 %}
|
|
<h3>
|
|
Main Request <small>{{ collector.events.__section__.duration }} ms</small>
|
|
</h3>
|
|
{% endif %}
|
|
|
|
{{ helper.display_timeline(token, collector.events, collector.events.__section__.origin) }}
|
|
|
|
{% if profile.children|length %}
|
|
<p class="help">Note: sections with a striped background correspond to sub-requests.</p>
|
|
|
|
<h3>Sub-requests <small>({{ profile.children|length }})</small></h3>
|
|
|
|
{% for child in profile.children %}
|
|
{% set events = child.getcollector('time').events %}
|
|
<h4>
|
|
<a href="{{ path('_profiler', { token: child.token, panel: 'time' }) }}">{{ child.getcollector('request').identifier }}</a>
|
|
<small>{{ events.__section__.duration }} ms</small>
|
|
</h4>
|
|
|
|
{{ helper.display_timeline(child.token, events, collector.events.__section__.origin) }}
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<svg id="timeline-template" width="0" height="0">
|
|
<defs>
|
|
<pattern id="subrequest" class="timeline-subrequest-pattern" patternUnits="userSpaceOnUse" width="20" height="20" viewBox="0 0 40 40">
|
|
<path d="M0 40L40 0H20L0 20M40 40V20L20 40"/>
|
|
</pattern>
|
|
</defs>
|
|
</svg>
|
|
<style type="text/css">
|
|
{% include '@WebProfiler/Collector/time.css.twig' %}
|
|
</style>
|
|
<script>
|
|
{% include '@WebProfiler/Collector/time.js' %}
|
|
</script>
|
|
{% endblock %}
|
|
|
|
{% macro dump_request_data(token, events, origin) %}
|
|
{% autoescape 'js' %}
|
|
{% from _self import dump_events %}
|
|
{
|
|
id: "{{ token }}",
|
|
left: {{ "%F"|format(events.__section__.origin - origin) }},
|
|
end: "{{ '%F'|format(events.__section__.endtime) }}",
|
|
events: [ {{ dump_events(events) }} ],
|
|
}
|
|
{% endautoescape %}
|
|
{% endmacro %}
|
|
|
|
{% macro dump_events(events) %}
|
|
{% autoescape 'js' %}
|
|
{% for name, event in events %}
|
|
{% if '__section__' != name %}
|
|
{
|
|
name: "{{ name }}",
|
|
category: "{{ event.category }}",
|
|
origin: {{ "%F"|format(event.origin) }},
|
|
starttime: {{ "%F"|format(event.starttime) }},
|
|
endtime: {{ "%F"|format(event.endtime) }},
|
|
duration: {{ "%F"|format(event.duration) }},
|
|
memory: {{ "%.1F"|format(event.memory / 1024 / 1024) }},
|
|
elements: {},
|
|
periods: [
|
|
{%- for period in event.periods -%}
|
|
{
|
|
start: {{ "%F"|format(period.starttime) }},
|
|
end: {{ "%F"|format(period.endtime) }},
|
|
duration: {{ "%F"|format(period.duration) }},
|
|
elements: {}
|
|
},
|
|
{%- endfor -%}
|
|
],
|
|
},
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% endautoescape %}
|
|
{% endmacro %}
|
|
|
|
{% macro display_timeline(token, events, origin) %}
|
|
{% import _self as helper %}
|
|
<div class="sf-profiler-timeline">
|
|
<div id="legend-{{ token }}" class="legends"></div>
|
|
<svg id="timeline-{{ token }}" class="timeline-graph"></svg>
|
|
<script>{% autoescape 'js' %}
|
|
window.addEventListener('load', function onLoad() {
|
|
const theme = new Theme();
|
|
|
|
new TimelineEngine(
|
|
theme,
|
|
new SvgRenderer(document.getElementById('timeline-{{ token }}')),
|
|
new Legend(document.getElementById('legend-{{ token }}'), theme),
|
|
document.getElementById('threshold'),
|
|
{{ helper.dump_request_data(token, events, origin) }}
|
|
);
|
|
});
|
|
{% endautoescape %}</script>
|
|
</div>
|
|
{% endmacro %}
|